From 3ede48f49f3884c809af1b6f32f584a97bf001b8 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Mon, 15 Jan 2024 22:06:51 +0000 Subject: [PATCH 001/203] server: Add unimplemented AI handler --- go.mod | 35 ++++++++++++++------ go.sum | 91 +++++++++++++++++++++++++++++++++++++-------------- server/rpc.go | 7 ++++ 3 files changed, 99 insertions(+), 34 deletions(-) diff --git a/go.mod b/go.mod index ed9e2f371..dddf11c38 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 + github.com/livepeer/ai-worker v0.0.0-20240115213021-46e59c289f64 github.com/livepeer/go-tools v0.0.0-20220805063103-76df6beb6506 github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240115103113-98566e26c007 @@ -26,7 +27,7 @@ require ( github.com/urfave/cli v1.22.12 go.opencensus.io v0.24.0 go.uber.org/goleak v1.3.0 - golang.org/x/net v0.17.0 + golang.org/x/net v0.19.0 google.golang.org/grpc v1.57.1 pgregory.net/rapid v1.1.0 ) @@ -42,6 +43,7 @@ require ( github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Microsoft/hcsshim v0.11.1 // indirect github.com/StackExchange/wmi v1.2.1 // indirect + github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/aws/aws-sdk-go v1.44.64 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.7.0 // indirect @@ -59,9 +61,11 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/deckarep/golang-set/v2 v2.1.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect + github.com/distribution/reference v0.5.0 // indirect github.com/dlclark/regexp2 v1.7.0 // indirect - github.com/docker/distribution v2.8.2+incompatible // indirect - github.com/docker/docker v24.0.6+incompatible // indirect + github.com/docker/cli v24.0.5+incompatible // indirect + github.com/docker/distribution v2.8.3+incompatible // indirect + github.com/docker/docker v24.0.7+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127 // indirect @@ -69,10 +73,14 @@ require ( github.com/fatih/color v1.13.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect + github.com/getkin/kin-openapi v0.118.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect + github.com/go-chi/chi/v5 v5.0.11 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/swag v0.19.5 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect @@ -80,19 +88,22 @@ require ( github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect - github.com/google/uuid v1.3.1 // indirect + github.com/google/uuid v1.5.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect github.com/googleapis/gax-go/v2 v2.7.1 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/holiman/uint256 v1.2.3 // indirect + github.com/invopop/yaml v0.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/josharian/intern v1.0.0 // indirect github.com/karalabe/usb v0.0.2 // indirect - github.com/klauspost/compress v1.16.3 // indirect + github.com/klauspost/compress v1.16.7 // indirect github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/magiconair/properties v1.8.7 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect @@ -100,10 +111,13 @@ require ( github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/sys/sequential v0.5.0 // indirect github.com/moby/term v0.5.0 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/morikuni/aec v1.0.0 // indirect + github.com/oapi-codegen/runtime v1.1.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc5 // indirect github.com/opencontainers/runc v1.1.5 // indirect + github.com/perimeterx/marshmallow v1.1.4 // indirect github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect @@ -125,14 +139,15 @@ require ( github.com/supranational/blst v0.3.11 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect + github.com/vincent-petithory/dataurl v1.0.0 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect - golang.org/x/crypto v0.14.0 // indirect + golang.org/x/crypto v0.17.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/mod v0.12.0 // indirect golang.org/x/oauth2 v0.7.0 // indirect golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.13.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.114.0 // indirect @@ -140,7 +155,7 @@ require ( google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect - google.golang.org/protobuf v1.30.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect howett.net/plist v1.0.0 // indirect diff --git a/go.sum b/go.sum index 5f01dac8e..9e78dc25b 100644 --- a/go.sum +++ b/go.sum @@ -57,6 +57,7 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Microsoft/hcsshim v0.11.1 h1:hJ3s7GbWlGK4YVV92sO88BQSyF4ZLVy7/awqOlPxFbA= github.com/Microsoft/hcsshim v0.11.1/go.mod h1:nFJmaO4Zr5Y7eADdFOpYswDDlNVbvcIJJNJLECr5JQg= +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= @@ -66,6 +67,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= github.com/aws/aws-sdk-go v1.44.64 h1:DuDZSBDkFBWW5H8q6i80RJDkBaaa/53KA6Jreqwjlqw= github.com/aws/aws-sdk-go v1.44.64/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -74,6 +77,7 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= @@ -133,13 +137,17 @@ github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= +github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo= github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= -github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v24.0.6+incompatible h1:hceabKCtUgDqPu+qm0NgsaXf28Ljf4/pWFL7xjWWDgE= -github.com/docker/docker v24.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/cli v24.0.5+incompatible h1:WeBimjvS0eKdH4Ygx+ihVq1Q++xg36M/rMi4aXAvodc= +github.com/docker/cli v24.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= +github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= +github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= @@ -167,8 +175,12 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= 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/getkin/kin-openapi v0.118.0 h1:z43njxPmJ7TaPpMSCQb7PN0dEYno4tyBPQcrFdHoLuM= +github.com/getkin/kin-openapi v0.118.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-chi/chi/v5 v5.0.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA= +github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= 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= @@ -187,11 +199,17 @@ github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= +github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= +github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= @@ -269,14 +287,15 @@ github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8q github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.7.1 h1:gF4c0zjUP2H/s/hEGyLA3I0fA2ZWjzYiONAD6cvPr8A= github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= @@ -289,6 +308,8 @@ github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZm github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= +github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc= +github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jaypipes/ghw v0.10.0 h1:UHu9UX08Py315iPojADFPOkmjTsNzHj4g4adsNKKteY= github.com/jaypipes/ghw v0.10.0/go.mod h1:jeJGbkRB2lL3/gxYzNYzEDETV1ZJ56OKr+CSeSEym+g= @@ -299,6 +320,8 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -306,14 +329,15 @@ github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/karalabe/usb v0.0.2 h1:M6QQBNxF+CQ8OFvxrT90BA0qBOXymndZnk5q235mFc4= github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY= -github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= +github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -327,14 +351,14 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= +github.com/livepeer/ai-worker v0.0.0-20240115213021-46e59c289f64 h1:3xjnPs5cTsNT7RBJ8cc982uN/5GgOFxhdptmjEifT1E= +github.com/livepeer/ai-worker v0.0.0-20240115213021-46e59c289f64/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= github.com/livepeer/go-tools v0.0.0-20220805063103-76df6beb6506 h1:qKon23c1RQPvL5Oya/hkImbaXNMkt6VdYtnh5jcIhoY= github.com/livepeer/go-tools v0.0.0-20220805063103-76df6beb6506/go.mod h1:aLVS1DT0ur9kpr0IlNI4DNcm9vVjRRUjDnwuEUm0BdQ= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded/go.mod h1:xkDdm+akniYxVT9KW1Y2Y7Hso6aW+rZObz3nrA9yTHw= github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 h1:4oH3NqV0NvcdS44Ld3zK2tO8IUiNozIggm74yobQeZg= github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18/go.mod h1:Jpf4jHK+fbWioBHRDRM1WadNT1qmY27g2YicTdO0Rtc= -github.com/livepeer/lpms v0.0.0-20231002131146-663c62246a3c h1:qxmMX/j6Yp0xFR17nhGktbHwEVqIjLK5lh2TiiENzY8= -github.com/livepeer/lpms v0.0.0-20231002131146-663c62246a3c/go.mod h1:Hr/JhxxPDipOVd4ZrGYWrdJfpVF8/SEI0nNr2ctAlkM= github.com/livepeer/lpms v0.0.0-20240115103113-98566e26c007 h1:0xr1TeIanBDdzI3sE2Zgf2yEV3s+6cPo3lJeyOHKmtM= github.com/livepeer/lpms v0.0.0-20240115103113-98566e26c007/go.mod h1:Hr/JhxxPDipOVd4ZrGYWrdJfpVF8/SEI0nNr2ctAlkM= github.com/livepeer/m3u8 v0.11.1 h1:VkUJzfNTyjy9mqsgp5JPvouwna8wGZMvd/gAfT5FinU= @@ -343,14 +367,18 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= @@ -379,11 +407,15 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= +github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs= @@ -398,6 +430,8 @@ github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.m github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/perimeterx/marshmallow v1.1.4 h1:pZLDH9RjlLGGorbXhcaQLhfuV0pFMNfPO55FuFkxqLw= +github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/peterbourgon/ff/v3 v3.4.0 h1:QBvM/rizZM1cB0p0lGMdmR7HxZeI/ZrBWB4DqLkMUBc= github.com/peterbourgon/ff/v3 v3.4.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM= @@ -473,6 +507,7 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -502,10 +537,16 @@ github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0h github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= +github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= +github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8= github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= +github.com/vincent-petithory/dataurl v1.0.0 h1:cXw+kPto8NLuJtlMsI152irrVw9fRDX8AbShPRpg2CI= +github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= @@ -535,8 +576,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -610,8 +651,8 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -695,11 +736,12 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -710,12 +752,12 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -859,8 +901,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -879,9 +921,10 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/server/rpc.go b/server/rpc.go index 4f278d06f..071a8b131 100644 --- a/server/rpc.go +++ b/server/rpc.go @@ -16,6 +16,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials" + "github.com/livepeer/ai-worker/worker" "github.com/livepeer/go-livepeer/clog" "github.com/livepeer/go-livepeer/common" "github.com/livepeer/go-livepeer/core" @@ -199,6 +200,12 @@ func StartTranscodeServer(orch Orchestrator, bind string, mux *http.ServeMux, wo lp.transRPC.HandleFunc("/transcodeResults", lp.TranscodeResults) } + // TODO: Inject worker.ServerInterface with actual implementation + aiHandler := worker.Handler(worker.Unimplemented{}) + lp.transRPC.Handle("/text-to-image", aiHandler) + lp.transRPC.Handle("/image-to-image", aiHandler) + lp.transRPC.Handle("/image-to-video", aiHandler) + cert, key, err := getCert(orch.ServiceURI(), workDir) if err != nil { return err From 6f29698587af96d431bfefda6dbeeacb98be8d0c Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Wed, 17 Jan 2024 20:41:40 +0000 Subject: [PATCH 002/203] multi: Add /text-to-image for O --- cmd/livepeer/livepeer.go | 5 +- cmd/livepeer/starter/starter.go | 57 +++++++++++++ core/livepeernode.go | 4 + core/orchestrator.go | 9 ++ go.mod | 29 ++----- go.sum | 143 +++++++++++++++++++++++--------- server/ai_http.go | 49 +++++++++++ server/rpc.go | 9 +- 8 files changed, 241 insertions(+), 64 deletions(-) create mode 100644 server/ai_http.go diff --git a/cmd/livepeer/livepeer.go b/cmd/livepeer/livepeer.go index b9ed7a907..de1562c2c 100755 --- a/cmd/livepeer/livepeer.go +++ b/cmd/livepeer/livepeer.go @@ -104,7 +104,7 @@ func main() { case sig := <-c: glog.Infof("Exiting Livepeer: %v", sig) cancel() - time.Sleep(time.Millisecond * 500) //Give time for other processes to shut down completely + time.Sleep(time.Second * 2) //Give time for other processes to shut down completely case <-lc: } } @@ -149,6 +149,9 @@ func parseLivepeerConfig() starter.LivepeerConfig { cfg.Netint = flag.String("netint", *cfg.Netint, "Comma-separated list of NetInt device GUIDs (or \"all\" for all available devices)") cfg.TestTranscoder = flag.Bool("testTranscoder", *cfg.TestTranscoder, "Test Nvidia GPU transcoding at startup") + // AI: + cfg.AIWorker = flag.Bool("aiworker", *cfg.AIWorker, "Set to true to run an AI worker") + // Onchain: cfg.EthAcctAddr = flag.String("ethAcctAddr", *cfg.EthAcctAddr, "Existing Eth account address. For use when multiple ETH accounts exist in the keystore directory") cfg.EthPassword = flag.String("ethPassword", *cfg.EthPassword, "Password for existing Eth account address or path to file") diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index 8c5da0bf2..d8d8d8bd6 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -14,9 +14,11 @@ import ( "net/url" "os" "os/user" + "path" "path/filepath" "strconv" "strings" + "sync" "time" ethcommon "github.com/ethereum/go-ethereum/common" @@ -25,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" "github.com/golang/glog" + "github.com/livepeer/ai-worker/worker" "github.com/livepeer/go-livepeer/build" "github.com/livepeer/go-livepeer/common" "github.com/livepeer/go-livepeer/core" @@ -58,6 +61,9 @@ var ( cleanupInterval = 10 * time.Minute // The time to live for cached max float values for PM senders (else they will be cleaned up) in seconds smTTL = 172800 // 2 days + + aiWorkerContainerImageID = "runner" + aiWorkerContainerStopTimeout = 5 * time.Second ) const ( @@ -85,6 +91,7 @@ type LivepeerConfig struct { HttpIngest *bool Orchestrator *bool Transcoder *bool + AIWorker *bool Broadcaster *bool OrchSecret *string TranscodingOptions *string @@ -175,6 +182,9 @@ func DefaultLivepeerConfig() LivepeerConfig { defaultNetint := "" defaultTestTranscoder := true + // AI: + defaultAIWorker := false + // Onchain: defaultEthAcctAddr := "" defaultEthPassword := "" @@ -259,6 +269,9 @@ func DefaultLivepeerConfig() LivepeerConfig { Netint: &defaultNetint, TestTranscoder: &defaultTestTranscoder, + // AI: + AIWorker: &defaultAIWorker, + // Onchain: EthAcctAddr: &defaultEthAcctAddr, EthPassword: &defaultEthPassword, @@ -478,6 +491,50 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { } } + if *cfg.AIWorker { + if *cfg.Nvidia == "" { + glog.Error("-nvidia required when using -aiworker") + return + } + + modelDir := path.Join(*cfg.Datadir, "models") + n.AIWorker, err = worker.NewWorker(aiWorkerContainerImageID, *cfg.Nvidia, modelDir) + if err != nil { + glog.Errorf("Error starting AI worker: %v", err) + return + } + + warmPipelines := map[string]string{ + "text-to-image": "stabilityai/sd-turbo", + } + + for pipeline, modelID := range warmPipelines { + if err := n.AIWorker.Warm(ctx, pipeline, modelID); err != nil { + glog.Errorf("Error AI worker warming %v container: %v", pipeline, err) + return + } + } + + defer func() { + var stopContainerWg sync.WaitGroup + for pipeline := range warmPipelines { + stopContainerWg.Add(1) + go func(pipeline string) { + defer stopContainerWg.Done() + ctx, cancel := context.WithTimeout(context.Background(), aiWorkerContainerStopTimeout) + defer cancel() + if err := n.AIWorker.Stop(ctx, pipeline); err != nil { + glog.Errorf("Error AI worker stopping %v container: %v", pipeline, err) + return + } + }(pipeline) + } + + stopContainerWg.Wait() + glog.Infof("Stopped AI worker containers") + }() + } + if *cfg.Redeemer { n.NodeType = core.RedeemerNode } else if *cfg.Orchestrator { diff --git a/core/livepeernode.go b/core/livepeernode.go index 57b1055e3..9e630f4c9 100644 --- a/core/livepeernode.go +++ b/core/livepeernode.go @@ -19,6 +19,7 @@ import ( "time" "github.com/golang/glog" + "github.com/livepeer/ai-worker/worker" "github.com/livepeer/go-livepeer/pm" "github.com/livepeer/go-livepeer/common" @@ -72,6 +73,9 @@ type LivepeerNode struct { NodeType NodeType Database *common.DB + // AI worker public fields + AIWorker *worker.Worker + // Transcoder public fields SegmentChans map[ManifestID]SegmentChan Recipient pm.Recipient diff --git a/core/orchestrator.go b/core/orchestrator.go index f5eeb121c..7193388cd 100644 --- a/core/orchestrator.go +++ b/core/orchestrator.go @@ -20,6 +20,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/golang/glog" + "github.com/livepeer/ai-worker/worker" "github.com/livepeer/go-livepeer/clog" "github.com/livepeer/go-livepeer/common" "github.com/livepeer/go-livepeer/eth" @@ -102,6 +103,10 @@ func (orch *orchestrator) TranscoderResults(tcID int64, res *RemoteTranscoderRes orch.node.TranscoderManager.transcoderResults(tcID, res) } +func (orch *orchestrator) TextToImage(ctx context.Context, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) { + return orch.node.textToImage(ctx, req) +} + func (orch *orchestrator) ProcessPayment(ctx context.Context, payment net.Payment, manifestID ManifestID) error { if orch.node == nil || orch.node.Recipient == nil { return nil @@ -751,6 +756,10 @@ func (n *LivepeerNode) serveTranscoder(stream net.Transcoder_RegisterTranscoderS } } +func (n *LivepeerNode) textToImage(ctx context.Context, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) { + return n.AIWorker.TextToImage(ctx, req) +} + func (rtm *RemoteTranscoderManager) transcoderResults(tcID int64, res *RemoteTranscoderResult) { remoteChan, err := rtm.getTaskChan(tcID) if err != nil { diff --git a/go.mod b/go.mod index dddf11c38..f6a51851e 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/livepeer/go-livepeer -go 1.20 +go 1.21.5 require ( contrib.go.opencensus.io/exporter/prometheus v0.4.2 @@ -11,19 +11,20 @@ require ( github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.0-20240115213021-46e59c289f64 + github.com/livepeer/ai-worker v0.0.0-20240117201450-dc36f66cc02a github.com/livepeer/go-tools v0.0.0-20220805063103-76df6beb6506 github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240115103113-98566e26c007 github.com/livepeer/m3u8 v0.11.1 github.com/mattn/go-sqlite3 v1.14.18 + github.com/oapi-codegen/nethttp-middleware v1.0.1 github.com/olekukonko/tablewriter v0.0.5 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/peterbourgon/ff/v3 v3.4.0 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.14.0 github.com/stretchr/testify v1.8.4 - github.com/testcontainers/testcontainers-go v0.26.0 + github.com/testcontainers/testcontainers-go v0.9.0 github.com/urfave/cli v1.22.12 go.opencensus.io v0.24.0 go.uber.org/goleak v1.3.0 @@ -38,24 +39,19 @@ require ( cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v0.13.0 // indirect cloud.google.com/go/storage v1.28.1 // indirect - dario.cat/mergo v1.0.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/Microsoft/hcsshim v0.11.1 // indirect github.com/StackExchange/wmi v1.2.1 // indirect github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/aws/aws-sdk-go v1.44.64 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.7.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect - github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/cp v1.1.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/consensys/bavard v0.1.13 // indirect github.com/consensys/gnark-crypto v0.12.1 // indirect - github.com/containerd/containerd v1.7.7 // indirect - github.com/containerd/log v0.1.0 // indirect - github.com/cpuguy83/dockercfg v0.3.1 // indirect + github.com/containerd/containerd v1.7.0-beta.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -79,8 +75,8 @@ require ( github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-ole/go-ole v1.2.6 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/swag v0.19.5 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/swag v0.22.4 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect @@ -91,16 +87,15 @@ require ( github.com/google/uuid v1.5.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect github.com/googleapis/gax-go/v2 v2.7.1 // indirect + github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/holiman/uint256 v1.2.3 // indirect - github.com/invopop/yaml v0.1.0 // indirect + github.com/invopop/yaml v0.2.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/karalabe/usb v0.0.2 // indirect github.com/klauspost/compress v1.16.7 // indirect github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded // indirect - github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect - github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -110,9 +105,7 @@ require ( github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/sys/sequential v0.5.0 // indirect - github.com/moby/term v0.5.0 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect - github.com/morikuni/aec v1.0.0 // indirect github.com/oapi-codegen/runtime v1.1.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc5 // indirect @@ -121,7 +114,6 @@ require ( github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect @@ -131,8 +123,6 @@ require ( github.com/rivo/uniseg v0.2.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect - github.com/shirou/gopsutil/v3 v3.23.9 // indirect - github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/status-im/keycard-go v0.2.0 // indirect github.com/stretchr/objx v0.5.0 // indirect @@ -140,7 +130,6 @@ require ( github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/vincent-petithory/dataurl v1.0.0 // indirect - github.com/yusufpapurcu/wmi v1.2.3 // indirect golang.org/x/crypto v0.17.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/mod v0.12.0 // indirect diff --git a/go.sum b/go.sum index 9e78dc25b..71509a00d 100644 --- a/go.sum +++ b/go.sum @@ -30,6 +30,7 @@ cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1 cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= +cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -43,24 +44,28 @@ cloud.google.com/go/storage v1.28.1 h1:F5QDG5ChchaAVQhINh24U99OWHURqrW8OmQcGKXcb cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= contrib.go.opencensus.io/exporter/prometheus v0.4.2 h1:sqfsYl5GIY/L570iT+l93ehxaWJs2/OwXtiWwew3oAg= contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9fpw1KeYcjrnC1J8B+JKjsZyRQ= -dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= -dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20221206110420-d395f97c4830 h1:u8scGKApGy+gXpYDw2f+nh60R0FqCfrpDRIQki+5o3U= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20221206110420-d395f97c4830/go.mod h1:VzwV+t+dZ9j/H867F1M2ziD+yLHtB46oM35FxxMJ4d0= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= +github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/Microsoft/hcsshim v0.11.1 h1:hJ3s7GbWlGK4YVV92sO88BQSyF4ZLVy7/awqOlPxFbA= -github.com/Microsoft/hcsshim v0.11.1/go.mod h1:nFJmaO4Zr5Y7eADdFOpYswDDlNVbvcIJJNJLECr5JQg= +github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/Microsoft/hcsshim v0.10.0-rc.1 h1:Lms8jwpaIdIUvoBNee8ZuvIi1XnNy9uvnxSC9L1q1x4= +github.com/Microsoft/hcsshim v0.10.0-rc.1/go.mod h1:7XX96hdvnwWGdXnksDNdhfFcUH1BtQY6bL2L3f9Abyk= github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= +github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -81,10 +86,9 @@ github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvF github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= -github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= @@ -103,30 +107,34 @@ github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2u github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/errors v1.8.1 h1:A5+txlVZfOqFBDa4mGz2bUWSp0aHElvHX2bKkdbQu+Y= +github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac= github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY= +github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 h1:aPEJyR4rPBvDmeyi+l/FS/VtA00IWvjeFvjen1m1l1A= +github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo= github.com/cockroachdb/redact v1.0.8 h1:8QG/764wK+vmEYoOlfobpe12EQcS81ukx/a4hdVMxNw= +github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 h1:IKgmqgMQlVJIZj19CdocBeSfSaiCbEBZGKODaixqtHM= +github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= -github.com/containerd/containerd v1.7.7 h1:QOC2K4A42RQpcrZyptP6z9EJZnlHfHJUfZrAAHe15q4= -github.com/containerd/containerd v1.7.7/go.mod h1:3c4XZv6VeT9qgf9GMTxNTMFxGJrGpI2vz1yk4ye+YY8= -github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= -github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.7.0-beta.2 h1:GWmC96y8j7jlFJX0Wh+covft0M1hHBqQL7lo+N6qvxg= +github.com/containerd/containerd v1.7.0-beta.2/go.mod h1:RR01Jsm/jovDKK48sFCVqWyKAH2APMPi88Aeu1on63I= +github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= -github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -144,12 +152,15 @@ github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/docker/cli v24.0.5+incompatible h1:WeBimjvS0eKdH4Ygx+ihVq1Q++xg36M/rMi4aXAvodc= github.com/docker/cli v24.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v17.12.0-ce-rc1.0.20200916142827-bd33bbf0497b+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= @@ -169,8 +180,11 @@ github.com/ethereum/go-ethereum v1.13.5/go.mod h1:yMTu38GSuyxaYzQMViqNmQ1s3cE84a github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= +github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.2 h1:SPb1KFFmM+ybpEjPUhCCkZOM5xlovT5UbrMvWnXyBns= +github.com/frankban/quicktest v1.14.2/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= @@ -179,6 +193,8 @@ github.com/getkin/kin-openapi v0.118.0 h1:z43njxPmJ7TaPpMSCQb7PN0dEYno4tyBPQcrFd github.com/getkin/kin-openapi v0.118.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/go-chi/chi/v5 v5.0.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA= github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -196,15 +212,27 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= +github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= +github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= @@ -213,10 +241,13 @@ github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncV github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.1.1 h1:jxpi2eWoU84wbX9iIEyAeeoac3FLuifZpY9tcNUD9kw= github.com/golang/glog v1.1.1/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= @@ -266,7 +297,6 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -275,6 +305,7 @@ github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPg github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -295,22 +326,32 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.7.1 h1:gF4c0zjUP2H/s/hEGyLA3I0fA2ZWjzYiONAD6cvPr8A= github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= +github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw= +github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= +github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= +github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= -github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc= github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= +github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= +github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= +github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jaypipes/ghw v0.10.0 h1:UHu9UX08Py315iPojADFPOkmjTsNzHj4g4adsNKKteY= github.com/jaypipes/ghw v0.10.0/go.mod h1:jeJGbkRB2lL3/gxYzNYzEDETV1ZJ56OKr+CSeSEym+g= github.com/jaypipes/pcidb v1.0.0 h1:vtZIfkiCUE42oYbJS0TAq9XSfSmcsgo9IdxSm9qzYU8= @@ -324,6 +365,7 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= @@ -345,14 +387,18 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= -github.com/livepeer/ai-worker v0.0.0-20240115213021-46e59c289f64 h1:3xjnPs5cTsNT7RBJ8cc982uN/5GgOFxhdptmjEifT1E= -github.com/livepeer/ai-worker v0.0.0-20240115213021-46e59c289f64/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= +github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/livepeer/ai-worker v0.0.0-20240117201450-dc36f66cc02a h1:lZoWT1Y2/R0VNdtFnP1bCOXuKVpam331blamx6SCxbo= +github.com/livepeer/ai-worker v0.0.0-20240117201450-dc36f66cc02a/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= github.com/livepeer/go-tools v0.0.0-20220805063103-76df6beb6506 h1:qKon23c1RQPvL5Oya/hkImbaXNMkt6VdYtnh5jcIhoY= github.com/livepeer/go-tools v0.0.0-20220805063103-76df6beb6506/go.mod h1:aLVS1DT0ur9kpr0IlNI4DNcm9vVjRRUjDnwuEUm0BdQ= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= @@ -363,10 +409,6 @@ github.com/livepeer/lpms v0.0.0-20240115103113-98566e26c007 h1:0xr1TeIanBDdzI3sE github.com/livepeer/lpms v0.0.0-20240115103113-98566e26c007/go.mod h1:Hr/JhxxPDipOVd4ZrGYWrdJfpVF8/SEI0nNr2ctAlkM= github.com/livepeer/m3u8 v0.11.1 h1:VkUJzfNTyjy9mqsgp5JPvouwna8wGZMvd/gAfT5FinU= github.com/livepeer/m3u8 v0.11.1/go.mod h1:IUqAtwWPAG2CblfQa4SVzTQoDcEMPyfNOaBSxqHMS04= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= @@ -391,7 +433,9 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfr github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= +github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= @@ -409,21 +453,33 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/oapi-codegen/nethttp-middleware v1.0.1 h1:ZWvwfnMU0eloHX1VEJmQscQm3741t0vCm0eSIie1NIo= +github.com/oapi-codegen/nethttp-middleware v1.0.1/go.mod h1:P7xtAvpoqNB+5obR9qRCeefH7YlXWSK3KgPs/9WB8tE= github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs= +github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.22.1 h1:pY8O4lBfsHKZHM/6nrxkhVPUznOlIu3quZcKP/M20KI= +github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/GDEs= github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= @@ -444,8 +500,6 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= @@ -487,19 +541,15 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/gopsutil/v3 v3.23.9 h1:ZI5bWVeu2ep4/DIxB4U9okeYJ7zp/QLTO4auRb/ty/E= -github.com/shirou/gopsutil/v3 v3.23.9/go.mod h1:x/NWSb71eMcjFIO0vhyGW5nZ7oSIgVjrCnADckb85GA= -github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= -github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= -github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= -github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -507,6 +557,7 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= @@ -530,34 +581,39 @@ github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbe github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= -github.com/testcontainers/testcontainers-go v0.26.0 h1:uqcYdoOHBy1ca7gKODfBd9uTHVK3a7UL848z09MVZ0c= -github.com/testcontainers/testcontainers-go v0.26.0/go.mod h1:ICriE9bLX5CLxL9OFQ2N+2N+f+803LNJ1utJb1+Inx0= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/testcontainers/testcontainers-go v0.9.0 h1:ZyftCfROjGrKlxk3MOUn2DAzWrUtzY/mj17iAkdUIvI= +github.com/testcontainers/testcontainers-go v0.9.0/go.mod h1:b22BFXhRbg4PJmeMVWh6ftqjyZHgiIl3w274e9r3C2E= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= +github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8= github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= +github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/vincent-petithory/dataurl v1.0.0 h1:cXw+kPto8NLuJtlMsI152irrVw9fRDX8AbShPRpg2CI= github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= -github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -616,6 +672,7 @@ golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -679,6 +736,7 @@ golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -715,7 +773,6 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -739,7 +796,6 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -758,6 +814,9 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180810170437-e96c4e24768d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -871,6 +930,7 @@ google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 h1: google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM= google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -910,7 +970,10 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -924,7 +987,11 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v0.0.0-20181223230014-1083505acf35 h1:zpdCK+REwbk+rqjJmHhiCN6iBIigrZ39glqSF0P3KF0= +gotest.tools v0.0.0-20181223230014-1083505acf35/go.mod h1:R//lfYlUuTOTfblYI3lGoAAAebUdzjvbmQsuB7Ykd90= gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/server/ai_http.go b/server/ai_http.go new file mode 100644 index 000000000..0546120f9 --- /dev/null +++ b/server/ai_http.go @@ -0,0 +1,49 @@ +package server + +import ( + "encoding/json" + "net/http" + + "github.com/livepeer/ai-worker/worker" + "github.com/livepeer/go-livepeer/clog" + "github.com/livepeer/go-livepeer/common" + middleware "github.com/oapi-codegen/nethttp-middleware" +) + +func startAIServer(lp lphttp) error { + swagger, err := worker.GetSwagger() + if err != nil { + return err + } + swagger.Servers = nil + + oapiReqValidator := middleware.OapiRequestValidator(swagger) + + aiHandler := worker.Handler(worker.Unimplemented{}) + lp.transRPC.Handle("/text-to-image", oapiReqValidator(lp.TextToImage())) + lp.transRPC.Handle("/image-to-image", aiHandler) + lp.transRPC.Handle("/image-to-video", aiHandler) + + return nil +} + +func (h *lphttp) TextToImage() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var req worker.TextToImageJSONRequestBody + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + clog.V(common.VERBOSE).Infof(r.Context(), "Received TextToImage request prompt=%v model_id=%v", req.Prompt, *req.ModelId) + + resp, err := h.orchestrator.TextToImage(r.Context(), req) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(resp) + }) +} diff --git a/server/rpc.go b/server/rpc.go index 071a8b131..ec8f6a683 100644 --- a/server/rpc.go +++ b/server/rpc.go @@ -61,6 +61,7 @@ type Orchestrator interface { DebitFees(addr ethcommon.Address, manifestID core.ManifestID, price *net.PriceInfo, pixels int64) Capabilities() *net.Capabilities AuthToken(sessionID string, expiration int64) *net.AuthToken + TextToImage(ctx context.Context, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) } // Balance describes methods for a session's balance maintenance @@ -200,11 +201,9 @@ func StartTranscodeServer(orch Orchestrator, bind string, mux *http.ServeMux, wo lp.transRPC.HandleFunc("/transcodeResults", lp.TranscodeResults) } - // TODO: Inject worker.ServerInterface with actual implementation - aiHandler := worker.Handler(worker.Unimplemented{}) - lp.transRPC.Handle("/text-to-image", aiHandler) - lp.transRPC.Handle("/image-to-image", aiHandler) - lp.transRPC.Handle("/image-to-video", aiHandler) + if n.AIWorker != nil { + startAIServer(lp) + } cert, key, err := getCert(orch.ServiceURI(), workDir) if err != nil { From e2735cd14e5f8220ac839921ae524af7c48808f6 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Wed, 17 Jan 2024 21:26:26 +0000 Subject: [PATCH 003/203] core+server: Add /image-to-image to O --- core/orchestrator.go | 8 ++++++++ server/ai_http.go | 33 ++++++++++++++++++++++++++++++++- server/rpc.go | 1 + 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/core/orchestrator.go b/core/orchestrator.go index 7193388cd..7c754a6a5 100644 --- a/core/orchestrator.go +++ b/core/orchestrator.go @@ -107,6 +107,10 @@ func (orch *orchestrator) TextToImage(ctx context.Context, req worker.TextToImag return orch.node.textToImage(ctx, req) } +func (orch *orchestrator) ImageToImage(ctx context.Context, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { + return orch.node.imageToImage(ctx, req) +} + func (orch *orchestrator) ProcessPayment(ctx context.Context, payment net.Payment, manifestID ManifestID) error { if orch.node == nil || orch.node.Recipient == nil { return nil @@ -760,6 +764,10 @@ func (n *LivepeerNode) textToImage(ctx context.Context, req worker.TextToImageJS return n.AIWorker.TextToImage(ctx, req) } +func (n *LivepeerNode) imageToImage(ctx context.Context, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { + return n.AIWorker.ImageToImage(ctx, req) +} + func (rtm *RemoteTranscoderManager) transcoderResults(tcID int64, res *RemoteTranscoderResult) { remoteChan, err := rtm.getTaskChan(tcID) if err != nil { diff --git a/server/ai_http.go b/server/ai_http.go index 0546120f9..15d966b7c 100644 --- a/server/ai_http.go +++ b/server/ai_http.go @@ -4,10 +4,12 @@ import ( "encoding/json" "net/http" + "github.com/getkin/kin-openapi/openapi3filter" "github.com/livepeer/ai-worker/worker" "github.com/livepeer/go-livepeer/clog" "github.com/livepeer/go-livepeer/common" middleware "github.com/oapi-codegen/nethttp-middleware" + "github.com/oapi-codegen/runtime" ) func startAIServer(lp lphttp) error { @@ -19,9 +21,11 @@ func startAIServer(lp lphttp) error { oapiReqValidator := middleware.OapiRequestValidator(swagger) + openapi3filter.RegisterBodyDecoder("image/png", openapi3filter.FileBodyDecoder) + aiHandler := worker.Handler(worker.Unimplemented{}) lp.transRPC.Handle("/text-to-image", oapiReqValidator(lp.TextToImage())) - lp.transRPC.Handle("/image-to-image", aiHandler) + lp.transRPC.Handle("/image-to-image", oapiReqValidator(lp.ImageToImage())) lp.transRPC.Handle("/image-to-video", aiHandler) return nil @@ -47,3 +51,30 @@ func (h *lphttp) TextToImage() http.Handler { _ = json.NewEncoder(w).Encode(resp) }) } + +func (h *lphttp) ImageToImage() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + multiRdr, err := r.MultipartReader() + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + var req worker.ImageToImageMultipartRequestBody + if err := runtime.BindMultipart(&req, *multiRdr); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + clog.V(common.VERBOSE).Infof(r.Context(), "Received ImageToImage request imageSize=%v prompt=%v model_id=%v", req.Image.FileSize(), req.Prompt, *req.ModelId) + + resp, err := h.orchestrator.ImageToImage(r.Context(), req) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(resp) + }) +} diff --git a/server/rpc.go b/server/rpc.go index ec8f6a683..ddfee3ed7 100644 --- a/server/rpc.go +++ b/server/rpc.go @@ -62,6 +62,7 @@ type Orchestrator interface { Capabilities() *net.Capabilities AuthToken(sessionID string, expiration int64) *net.AuthToken TextToImage(ctx context.Context, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) + ImageToImage(ctx context.Context, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) } // Balance describes methods for a session's balance maintenance From 8883824e2dca7c4c8bf4112efc72c6723ad2de96 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Wed, 17 Jan 2024 21:38:16 +0000 Subject: [PATCH 004/203] core+server: Add /image-to-video for O --- core/orchestrator.go | 8 ++++++++ server/ai_http.go | 30 ++++++++++++++++++++++++++++-- server/rpc.go | 1 + 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/core/orchestrator.go b/core/orchestrator.go index 7c754a6a5..7ea3cd4be 100644 --- a/core/orchestrator.go +++ b/core/orchestrator.go @@ -111,6 +111,10 @@ func (orch *orchestrator) ImageToImage(ctx context.Context, req worker.ImageToIm return orch.node.imageToImage(ctx, req) } +func (orch *orchestrator) ImageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) (*worker.VideoResponse, error) { + return orch.node.imageToVideo(ctx, req) +} + func (orch *orchestrator) ProcessPayment(ctx context.Context, payment net.Payment, manifestID ManifestID) error { if orch.node == nil || orch.node.Recipient == nil { return nil @@ -768,6 +772,10 @@ func (n *LivepeerNode) imageToImage(ctx context.Context, req worker.ImageToImage return n.AIWorker.ImageToImage(ctx, req) } +func (n *LivepeerNode) imageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) (*worker.VideoResponse, error) { + return n.AIWorker.ImageToVideo(ctx, req) +} + func (rtm *RemoteTranscoderManager) transcoderResults(tcID int64, res *RemoteTranscoderResult) { remoteChan, err := rtm.getTaskChan(tcID) if err != nil { diff --git a/server/ai_http.go b/server/ai_http.go index 15d966b7c..b2524606c 100644 --- a/server/ai_http.go +++ b/server/ai_http.go @@ -23,10 +23,9 @@ func startAIServer(lp lphttp) error { openapi3filter.RegisterBodyDecoder("image/png", openapi3filter.FileBodyDecoder) - aiHandler := worker.Handler(worker.Unimplemented{}) lp.transRPC.Handle("/text-to-image", oapiReqValidator(lp.TextToImage())) lp.transRPC.Handle("/image-to-image", oapiReqValidator(lp.ImageToImage())) - lp.transRPC.Handle("/image-to-video", aiHandler) + lp.transRPC.Handle("/image-to-video", oapiReqValidator(lp.ImageToVideo())) return nil } @@ -78,3 +77,30 @@ func (h *lphttp) ImageToImage() http.Handler { _ = json.NewEncoder(w).Encode(resp) }) } + +func (h *lphttp) ImageToVideo() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + multiRdr, err := r.MultipartReader() + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + var req worker.ImageToVideoMultipartRequestBody + if err := runtime.BindMultipart(&req, *multiRdr); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + clog.V(common.VERBOSE).Infof(r.Context(), "Received ImageToVideo request imageSize=%v model_id=%v", req.Image.FileSize(), *req.ModelId) + + resp, err := h.orchestrator.ImageToVideo(r.Context(), req) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(resp) + }) +} diff --git a/server/rpc.go b/server/rpc.go index ddfee3ed7..f8e1c4548 100644 --- a/server/rpc.go +++ b/server/rpc.go @@ -63,6 +63,7 @@ type Orchestrator interface { AuthToken(sessionID string, expiration int64) *net.AuthToken TextToImage(ctx context.Context, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) ImageToImage(ctx context.Context, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) + ImageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) (*worker.VideoResponse, error) } // Balance describes methods for a session's balance maintenance From 92bfa74f601a5e240d7956dff3dd1886ad83ebc2 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Sun, 21 Jan 2024 21:20:59 +0000 Subject: [PATCH 005/203] multi: Transcode PNG -> mp4 for image-to-video --- ai/file_worker.go | 83 ++++++++++++++++++++++ core/ai.go | 15 ++++ core/livepeernode.go | 3 +- core/orchestrator.go | 162 ++++++++++++++++++++++++++++++++++++++++++- core/streamdata.go | 1 + core/transcoder.go | 18 +++-- go.mod | 6 +- go.sum | 4 +- install_ffmpeg.sh | 6 +- server/ai_http.go | 59 +++++++++++++++- server/rpc.go | 2 +- 11 files changed, 337 insertions(+), 22 deletions(-) create mode 100644 ai/file_worker.go create mode 100644 core/ai.go diff --git a/ai/file_worker.go b/ai/file_worker.go new file mode 100644 index 000000000..40593977d --- /dev/null +++ b/ai/file_worker.go @@ -0,0 +1,83 @@ +package ai + +import ( + "context" + "encoding/json" + "errors" + "os" + + "github.com/livepeer/ai-worker/worker" +) + +type FileWorker struct { + files map[string]string +} + +func NewFileWorker(files map[string]string) *FileWorker { + return &FileWorker{files: files} +} + +func (w *FileWorker) TextToImage(ctx context.Context, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) { + fname, ok := w.files["text-to-image"] + if !ok { + return nil, errors.New("text-to-image response file not found") + } + + data, err := os.ReadFile(fname) + if err != nil { + return nil, err + } + + var resp worker.ImageResponse + if err := json.Unmarshal(data, &resp); err != nil { + return nil, err + } + + return &resp, nil +} + +func (w *FileWorker) ImageToImage(ctx context.Context, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { + fname, ok := w.files["image-to-image"] + if !ok { + return nil, errors.New("image-to-image response file not found") + } + + data, err := os.ReadFile(fname) + if err != nil { + return nil, err + } + + var resp worker.ImageResponse + if err := json.Unmarshal(data, &resp); err != nil { + return nil, err + } + + return &resp, nil +} + +func (w *FileWorker) ImageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) (*worker.VideoResponse, error) { + fname, ok := w.files["image-to-video"] + if !ok { + return nil, errors.New("image-to-video response file not found") + } + + data, err := os.ReadFile(fname) + if err != nil { + return nil, err + } + + var resp worker.VideoResponse + if err := json.Unmarshal(data, &resp); err != nil { + return nil, err + } + + return &resp, nil +} + +func (w *FileWorker) Warm(ctx context.Context, containerName, modelID string) error { + return nil +} + +func (w *FileWorker) Stop(ctx context.Context, containerName string) error { + return nil +} diff --git a/core/ai.go b/core/ai.go new file mode 100644 index 000000000..9d874f4f2 --- /dev/null +++ b/core/ai.go @@ -0,0 +1,15 @@ +package core + +import ( + "context" + + "github.com/livepeer/ai-worker/worker" +) + +type AI interface { + TextToImage(context.Context, worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) + ImageToImage(context.Context, worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) + ImageToVideo(context.Context, worker.ImageToVideoMultipartRequestBody) (*worker.VideoResponse, error) + Warm(context.Context, string, string) error + Stop(context.Context, string) error +} diff --git a/core/livepeernode.go b/core/livepeernode.go index 9e630f4c9..f4391b409 100644 --- a/core/livepeernode.go +++ b/core/livepeernode.go @@ -19,7 +19,6 @@ import ( "time" "github.com/golang/glog" - "github.com/livepeer/ai-worker/worker" "github.com/livepeer/go-livepeer/pm" "github.com/livepeer/go-livepeer/common" @@ -74,7 +73,7 @@ type LivepeerNode struct { Database *common.DB // AI worker public fields - AIWorker *worker.Worker + AIWorker AI // Transcoder public fields SegmentChans map[ManifestID]SegmentChan diff --git a/core/orchestrator.go b/core/orchestrator.go index 7ea3cd4be..185c343f8 100644 --- a/core/orchestrator.go +++ b/core/orchestrator.go @@ -13,6 +13,7 @@ import ( "os" "path" "sort" + "strconv" "sync" "time" @@ -31,6 +32,7 @@ import ( lpcrypto "github.com/livepeer/go-livepeer/crypto" lpmon "github.com/livepeer/go-livepeer/monitor" + "github.com/livepeer/lpms/ffmpeg" "github.com/livepeer/lpms/stream" ) @@ -111,7 +113,7 @@ func (orch *orchestrator) ImageToImage(ctx context.Context, req worker.ImageToIm return orch.node.imageToImage(ctx, req) } -func (orch *orchestrator) ImageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) (*worker.VideoResponse, error) { +func (orch *orchestrator) ImageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) ([]*TranscodeResult, error) { return orch.node.imageToVideo(ctx, req) } @@ -538,6 +540,124 @@ func (n *LivepeerNode) sendToTranscodeLoop(ctx context.Context, md *SegTranscodi return res, res.Err } +func (n *LivepeerNode) transcodeFrames(ctx context.Context, sessionID string, urls []string) *TranscodeResult { + ctx = clog.AddOrchSessionID(ctx, sessionID) + + var fnamep *string + terr := func(err error) *TranscodeResult { + if fnamep != nil { + if err := os.RemoveAll(*fnamep); err != nil { + clog.Errorf(ctx, "Transcoder failed to cleanup %v", *fnamep) + } + } + return &TranscodeResult{Err: err} + } + + // We only support base64 png data urls right now + // We will want to support HTTP and file urls later on as well + dirPath := path.Join(n.WorkDir, "input", sessionID+"_"+string(RandomManifestID())) + fnamep = &dirPath + if err := os.MkdirAll(dirPath, 0700); err != nil { + clog.Errorf(ctx, "Transcoder cannot create frames dir err=%q", err) + return terr(err) + } + for i, url := range urls { + fname := path.Join(dirPath, strconv.Itoa(i)+".png") + if err := worker.SaveImageB64DataUrl(url, fname); err != nil { + clog.Errorf(ctx, "Transcoder failed to save image from url err=%q", err) + return terr(err) + } + } + + // Check if there's a transcoder available + if n.Transcoder == nil { + return terr(ErrTranscoderAvail) + } + transcoder := n.Transcoder + + md := &SegTranscodingMetadata{ + Fname: path.Join(dirPath, "%d.png"), + ProfileIn: ffmpeg.VideoProfile{ + Framerate: 7, + FramerateDen: 1, + }, + Profiles: []ffmpeg.VideoProfile{ + { + Name: "image-to-video", + Resolution: "1024x576", + AspectRatio: "16:9", + Bitrate: "6000k", + Format: ffmpeg.FormatMP4, + }, + }, + AuthToken: &net.AuthToken{SessionId: sessionID}, + } + + los := drivers.NodeStorage.NewSession(sessionID) + + // TODO: Figure out a better way to end the OS session after a timeout than creating a new goroutine per request? + go func() { + ctx, cancel := transcodeLoopContext() + defer cancel() + <-ctx.Done() + los.EndSession() + clog.Infof(ctx, "Ended image-to-video session sessionID=%v", sessionID) + }() + + start := time.Now() + tData, err := transcoder.Transcode(ctx, md) + if err != nil { + if _, ok := err.(UnrecoverableError); ok { + panic(err) + } + clog.Errorf(ctx, "Error transcoding frames dirPath=%s err=%q", dirPath, err) + return terr(err) + } + + took := time.Since(start) + clog.V(common.DEBUG).Infof(ctx, "Transcoding frames took=%v", took) + + transcoder.EndTranscodingSession(md.AuthToken.SessionId) + + tSegments := tData.Segments + if len(tSegments) != len(md.Profiles) { + clog.Errorf(ctx, "Did not receive the correct number of transcoded segments; got %v expected %v", len(tSegments), + len(md.Profiles)) + return terr(fmt.Errorf("MismatchedSegments")) + } + + // Prepare the result object + var tr TranscodeResult + segHashes := make([][]byte, len(tSegments)) + + for i := range md.Profiles { + if tSegments[i].Data == nil || len(tSegments[i].Data) < 25 { + clog.Errorf(ctx, "Cannot find transcoded segment for bytes=%d", len(tSegments[i].Data)) + return terr(fmt.Errorf("ZeroSegments")) + } + clog.V(common.DEBUG).Infof(ctx, "Transcoded segment profile=%s bytes=%d", + md.Profiles[i].Name, len(tSegments[i].Data)) + hash := crypto.Keccak256(tSegments[i].Data) + segHashes[i] = hash + } + if err := os.RemoveAll(dirPath); err != nil { + clog.Errorf(ctx, "Transcoder failed to cleanup %v", dirPath) + } + tr.OS = los + tr.TranscodeData = tData + + if n == nil || n.Eth == nil { + return &tr + } + + segHash := crypto.Keccak256(segHashes...) + tr.Sig, tr.Err = n.Eth.Sign(segHash) + if tr.Err != nil { + clog.Errorf(ctx, "Unable to sign hash of transcoded segment hashes err=%q", tr.Err) + } + return &tr +} + func (n *LivepeerNode) transcodeSeg(ctx context.Context, config transcodeConfig, seg *stream.HLSSegment, md *SegTranscodingMetadata) *TranscodeResult { var fnamep *string terr := func(err error) *TranscodeResult { @@ -772,8 +892,44 @@ func (n *LivepeerNode) imageToImage(ctx context.Context, req worker.ImageToImage return n.AIWorker.ImageToImage(ctx, req) } -func (n *LivepeerNode) imageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) (*worker.VideoResponse, error) { - return n.AIWorker.ImageToVideo(ctx, req) +func (n *LivepeerNode) imageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) ([]*TranscodeResult, error) { + // We might support generating more than one video in the future (i.e. multiple input images/prompts) + numVideos := 1 + + // Generate frames + start := time.Now() + resp, err := n.AIWorker.ImageToVideo(ctx, req) + if err != nil { + return nil, err + } + + if len(resp.Frames) != numVideos { + return nil, fmt.Errorf("unexpected number of image-to-video outputs expected=%v actual=%v", numVideos, len(resp.Frames)) + } + + took := time.Since(start) + clog.V(common.DEBUG).Infof(ctx, "Generating frames took=%v", took) + + sessionID := string(RandomManifestID()) + // Transcode frames into segments. + results := make([]*TranscodeResult, len(resp.Frames)) + for i, batch := range resp.Frames { + // Create slice of frame urls for a batch + urls := make([]string, len(batch)) + for j, frame := range batch { + urls[j] = frame.Url + } + + // Transcode slice of frame urls into a segment + res := n.transcodeFrames(ctx, sessionID, urls) + if res.Err != nil { + return nil, res.Err + } + + results[i] = res + } + + return results, nil } func (rtm *RemoteTranscoderManager) transcoderResults(tcID int64, res *RemoteTranscoderResult) { diff --git a/core/streamdata.go b/core/streamdata.go index 6b2909e5c..b58153fff 100644 --- a/core/streamdata.go +++ b/core/streamdata.go @@ -59,6 +59,7 @@ type SegTranscodingMetadata struct { Fname string Seq int64 Hash ethcommon.Hash + ProfileIn ffmpeg.VideoProfile Profiles []ffmpeg.VideoProfile OS *net.OSInfo Duration time.Duration diff --git a/core/transcoder.go b/core/transcoder.go index e91aa5669..a34dab3f6 100644 --- a/core/transcoder.go +++ b/core/transcoder.go @@ -47,8 +47,9 @@ func (lt *LocalTranscoder) Transcode(ctx context.Context, md *SegTranscodingMeta // Set up in / out config in := &ffmpeg.TranscodeOptionsIn{ - Fname: md.Fname, - Accel: ffmpeg.Software, + Fname: md.Fname, + Accel: ffmpeg.Software, + Profile: md.ProfileIn, } profiles := md.Profiles opts := profilesToTranscodeOptions(lt.workDir, ffmpeg.Software, profiles, md.CalcPerceptualHash, md.SegmentParameters) @@ -129,10 +130,17 @@ func (nv *NvidiaTranscoder) Transcode(ctx context.Context, md *SegTranscodingMet // Returns UnrecoverableError instead of panicking to gracefully notify orchestrator about transcoder's failure defer recoverFromPanic(&retErr) + inAccel := ffmpeg.Nvidia + if filepath.Ext(md.Fname) == ".png" { + // If the input is a PNG file we need to use the software decoder + inAccel = ffmpeg.Software + } + in := &ffmpeg.TranscodeOptionsIn{ - Fname: md.Fname, - Accel: ffmpeg.Nvidia, - Device: nv.device, + Fname: md.Fname, + Accel: inAccel, + Device: nv.device, + Profile: md.ProfileIn, } profiles := md.Profiles out := profilesToTranscodeOptions(WorkDir, ffmpeg.Nvidia, profiles, md.CalcPerceptualHash, md.SegmentParameters) diff --git a/go.mod b/go.mod index f6a51851e..dbbefed51 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( contrib.go.opencensus.io/exporter/prometheus v0.4.2 github.com/cenkalti/backoff v2.2.1+incompatible github.com/ethereum/go-ethereum v1.13.5 + github.com/getkin/kin-openapi v0.118.0 github.com/golang/glog v1.1.1 github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.3 @@ -14,10 +15,11 @@ require ( github.com/livepeer/ai-worker v0.0.0-20240117201450-dc36f66cc02a github.com/livepeer/go-tools v0.0.0-20220805063103-76df6beb6506 github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 - github.com/livepeer/lpms v0.0.0-20240115103113-98566e26c007 + github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 github.com/livepeer/m3u8 v0.11.1 github.com/mattn/go-sqlite3 v1.14.18 github.com/oapi-codegen/nethttp-middleware v1.0.1 + github.com/oapi-codegen/runtime v1.1.1 github.com/olekukonko/tablewriter v0.0.5 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/peterbourgon/ff/v3 v3.4.0 @@ -69,7 +71,6 @@ require ( github.com/fatih/color v1.13.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect - github.com/getkin/kin-openapi v0.118.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-chi/chi/v5 v5.0.11 // indirect github.com/go-kit/log v0.2.1 // indirect @@ -106,7 +107,6 @@ require ( github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/sys/sequential v0.5.0 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect - github.com/oapi-codegen/runtime v1.1.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc5 // indirect github.com/opencontainers/runc v1.1.5 // indirect diff --git a/go.sum b/go.sum index 71509a00d..664f73a8e 100644 --- a/go.sum +++ b/go.sum @@ -405,8 +405,8 @@ github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cO github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded/go.mod h1:xkDdm+akniYxVT9KW1Y2Y7Hso6aW+rZObz3nrA9yTHw= github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 h1:4oH3NqV0NvcdS44Ld3zK2tO8IUiNozIggm74yobQeZg= github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18/go.mod h1:Jpf4jHK+fbWioBHRDRM1WadNT1qmY27g2YicTdO0Rtc= -github.com/livepeer/lpms v0.0.0-20240115103113-98566e26c007 h1:0xr1TeIanBDdzI3sE2Zgf2yEV3s+6cPo3lJeyOHKmtM= -github.com/livepeer/lpms v0.0.0-20240115103113-98566e26c007/go.mod h1:Hr/JhxxPDipOVd4ZrGYWrdJfpVF8/SEI0nNr2ctAlkM= +github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 h1:4A6geMb+HfxBBfaS24t8R3ddpEDfWbpx7NTQZMt5Fp4= +github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69/go.mod h1:Hr/JhxxPDipOVd4ZrGYWrdJfpVF8/SEI0nNr2ctAlkM= github.com/livepeer/m3u8 v0.11.1 h1:VkUJzfNTyjy9mqsgp5JPvouwna8wGZMvd/gAfT5FinU= github.com/livepeer/m3u8 v0.11.1/go.mod h1:IUqAtwWPAG2CblfQa4SVzTQoDcEMPyfNOaBSxqHMS04= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= diff --git a/install_ffmpeg.sh b/install_ffmpeg.sh index 03b4e0b30..bb99292ef 100755 --- a/install_ffmpeg.sh +++ b/install_ffmpeg.sh @@ -208,13 +208,13 @@ if [[ ! -e "$ROOT/ffmpeg/libavcodec/libavcodec.a" ]]; then ./configure ${TARGET_OS:-} $DISABLE_FFMPEG_COMPONENTS --fatal-warnings \ --enable-libx264 --enable-gpl \ --enable-protocol=rtmp,file,pipe \ - --enable-muxer=mpegts,hls,segment,mp4,hevc,matroska,webm,null --enable-demuxer=flv,mpegts,mp4,mov,webm,matroska \ + --enable-muxer=mpegts,hls,segment,mp4,hevc,matroska,webm,null --enable-demuxer=flv,mpegts,mp4,mov,webm,matroska,image2 \ --enable-bsf=h264_mp4toannexb,aac_adtstoasc,h264_metadata,h264_redundant_pps,hevc_mp4toannexb,extract_extradata \ - --enable-parser=aac,aac_latm,h264,hevc,vp8,vp9 \ + --enable-parser=aac,aac_latm,h264,hevc,vp8,vp9,png \ --enable-filter=abuffer,buffer,abuffersink,buffersink,afifo,fifo,aformat,format \ --enable-filter=aresample,asetnsamples,fps,scale,hwdownload,select,livepeer_dnn,signature \ --enable-encoder=aac,opus,libx264 \ - --enable-decoder=aac,opus,h264 \ + --enable-decoder=aac,opus,h264,png \ --extra-cflags="${EXTRA_CFLAGS} -I${ROOT}/compiled/include -I/usr/local/cuda/include" \ --extra-ldflags="${EXTRA_FFMPEG_LDFLAGS} -L${ROOT}/compiled/lib -L/usr/local/cuda/lib64" \ --prefix="$ROOT/compiled" \ diff --git a/server/ai_http.go b/server/ai_http.go index b2524606c..bfcf2554d 100644 --- a/server/ai_http.go +++ b/server/ai_http.go @@ -1,13 +1,18 @@ package server import ( + "bytes" "encoding/json" + "fmt" "net/http" "github.com/getkin/kin-openapi/openapi3filter" + "github.com/golang/protobuf/proto" "github.com/livepeer/ai-worker/worker" "github.com/livepeer/go-livepeer/clog" "github.com/livepeer/go-livepeer/common" + "github.com/livepeer/go-livepeer/core" + "github.com/livepeer/go-livepeer/net" middleware "github.com/oapi-codegen/nethttp-middleware" "github.com/oapi-codegen/runtime" ) @@ -80,6 +85,9 @@ func (h *lphttp) ImageToImage() http.Handler { func (h *lphttp) ImageToVideo() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + remoteAddr := getRemoteAddr(r) + ctx := clog.AddVal(r.Context(), clog.ClientIP, remoteAddr) + multiRdr, err := r.MultipartReader() if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) @@ -92,15 +100,60 @@ func (h *lphttp) ImageToVideo() http.Handler { return } - clog.V(common.VERBOSE).Infof(r.Context(), "Received ImageToVideo request imageSize=%v model_id=%v", req.Image.FileSize(), *req.ModelId) + clog.V(common.VERBOSE).Infof(ctx, "Received ImageToVideo request imageSize=%v model_id=%v", req.Image.FileSize(), *req.ModelId) - resp, err := h.orchestrator.ImageToVideo(r.Context(), req) + results, err := h.orchestrator.ImageToVideo(ctx, req) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // TODO: Handle more than one video + if len(results) != 1 { + http.Error(w, "failed to return results", http.StatusInternalServerError) + return + } + + res := results[0] + + // Assume only single rendition right now + seg := res.TranscodeData.Segments[0] + name := fmt.Sprintf("%v.mp4", core.RandomManifestID()) + segData := bytes.NewReader(seg.Data) + uri, err := res.OS.SaveData(ctx, name, segData, nil, 0) + if err != nil { + clog.Errorf(ctx, "Could not upload segment err=%q", err) + } + + var result net.TranscodeResult + if err != nil { + clog.Errorf(ctx, "Could not transcode err=%q", err) + result = net.TranscodeResult{Result: &net.TranscodeResult_Error{Error: err.Error()}} + } else { + result = net.TranscodeResult{ + Result: &net.TranscodeResult_Data{ + Data: &net.TranscodeData{ + Segments: []*net.TranscodedSegmentData{ + {Url: uri, Pixels: seg.Pixels}, + }, + Sig: res.Sig, + }, + }, + } + } + + tr := &net.TranscodeResult{ + Result: result.Result, + // TODO: Add other fields + } + + buf, err := proto.Marshal(tr) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) - _ = json.NewEncoder(w).Encode(resp) + w.Write(buf) }) } diff --git a/server/rpc.go b/server/rpc.go index f8e1c4548..60841a130 100644 --- a/server/rpc.go +++ b/server/rpc.go @@ -63,7 +63,7 @@ type Orchestrator interface { AuthToken(sessionID string, expiration int64) *net.AuthToken TextToImage(ctx context.Context, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) ImageToImage(ctx context.Context, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) - ImageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) (*worker.VideoResponse, error) + ImageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) ([]*core.TranscodeResult, error) } // Balance describes methods for a session's balance maintenance From be72c3703810d7da95674aa69c8d745e6c2cf96f Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Sun, 21 Jan 2024 22:57:59 +0000 Subject: [PATCH 006/203] server: Impl B -> O image-to-video --- server/ai_mediaserver.go | 79 ++++++++++++++++++++++ server/ai_process.go | 138 +++++++++++++++++++++++++++++++++++++++ server/mediaserver.go | 2 + 3 files changed, 219 insertions(+) create mode 100644 server/ai_mediaserver.go create mode 100644 server/ai_process.go diff --git a/server/ai_mediaserver.go b/server/ai_mediaserver.go new file mode 100644 index 000000000..f2a9eaefc --- /dev/null +++ b/server/ai_mediaserver.go @@ -0,0 +1,79 @@ +package server + +import ( + "encoding/json" + "net/http" + "time" + + "github.com/getkin/kin-openapi/openapi3filter" + "github.com/livepeer/ai-worker/worker" + "github.com/livepeer/go-livepeer/clog" + "github.com/livepeer/go-livepeer/common" + "github.com/livepeer/go-livepeer/core" + "github.com/livepeer/go-tools/drivers" + middleware "github.com/oapi-codegen/nethttp-middleware" + "github.com/oapi-codegen/runtime" +) + +func startAIMediaServer(ls *LivepeerServer) error { + swagger, err := worker.GetSwagger() + if err != nil { + return err + } + swagger.Servers = nil + + oapiReqValidator := middleware.OapiRequestValidator(swagger) + + openapi3filter.RegisterBodyDecoder("image/png", openapi3filter.FileBodyDecoder) + + ls.HTTPMux.Handle("/image-to-video", oapiReqValidator(ls.ImageToVideo())) + + return nil +} + +func (ls *LivepeerServer) ImageToVideo() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + remoteAddr := getRemoteAddr(r) + ctx := clog.AddVal(r.Context(), clog.ClientIP, remoteAddr) + + multiRdr, err := r.MultipartReader() + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + var req worker.ImageToVideoMultipartRequestBody + if err := runtime.BindMultipart(&req, *multiRdr); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + clog.V(common.VERBOSE).Infof(ctx, "Received ImageToVideo request imageSize=%v model_id=%v", req.Image.FileSize(), *req.ModelId) + + params := aiRequestParams{ + node: ls.LivepeerNode, + os: drivers.NodeStorage.NewSession(string(core.RandomManifestID())), + } + + start := time.Now() + urls, err := processImageToVideo(ctx, params, req) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + took := time.Since(start) + clog.Infof(ctx, "Processed ImageToVideo request imageSize=%v model_id=%v took=%v", req.Image.FileSize(), *req.ModelId, took) + + // HACK: Re-use worker.ImageResponse to return results + videos := make([]worker.Media, len(urls)) + for i, url := range urls { + videos[i] = worker.Media{ + Url: url, + } + } + + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(&worker.ImageResponse{Images: videos}) + }) +} diff --git a/server/ai_process.go b/server/ai_process.go new file mode 100644 index 000000000..498cdca59 --- /dev/null +++ b/server/ai_process.go @@ -0,0 +1,138 @@ +package server + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "mime/multipart" + "net/http" + "path/filepath" + "time" + + "github.com/golang/protobuf/proto" + "github.com/livepeer/ai-worker/worker" + "github.com/livepeer/go-livepeer/common" + "github.com/livepeer/go-livepeer/core" + "github.com/livepeer/go-livepeer/net" + "github.com/livepeer/go-tools/drivers" +) + +const imageToVideoTimeout = 5 * time.Minute + +type aiRequestParams struct { + node *core.LivepeerNode + os drivers.OSSession +} + +func processImageToVideo(ctx context.Context, params aiRequestParams, req worker.ImageToVideoMultipartRequestBody) ([]string, error) { + // Discover 1 orchestrator + // TODO: Discover multiple orchestrators + caps := core.NewCapabilities(core.DefaultCapabilities(), nil) + orchDesc, err := params.node.OrchestratorPool.GetOrchestrators(ctx, 1, newSuspender(), caps, common.ScoreAtLeast(0)) + if err != nil { + return nil, err + } + orchInfos := orchDesc.GetRemoteInfos() + + if len(orchInfos) == 0 { + return nil, errors.New("no orchestrators available") + } + + orchUrl := orchInfos[0].Transcoder + urls, err := submitImageToVideo(ctx, orchUrl, req) + if err != nil { + return nil, err + } + + newUrls := make([]string, len(urls)) + for i, url := range urls { + data, err := downloadSeg(ctx, url) + if err != nil { + return nil, err + } + + name := filepath.Base(url) + newUrl, err := params.os.SaveData(ctx, name, bytes.NewReader(data), nil, 0) + if err != nil { + return nil, err + } + + newUrls[i] = newUrl + } + + return newUrls, nil +} + +func submitImageToVideo(ctx context.Context, url string, req worker.ImageToVideoMultipartRequestBody) ([]string, error) { + var buf bytes.Buffer + mw := multipart.NewWriter(&buf) + writer, err := mw.CreateFormFile("image", req.Image.Filename()) + if err != nil { + return nil, err + } + imageSize := req.Image.FileSize() + imageRdr, err := req.Image.Reader() + if err != nil { + return nil, err + } + copied, err := io.Copy(writer, imageRdr) + if err != nil { + return nil, err + } + if copied != imageSize { + return nil, fmt.Errorf("failed to copy image to multipart request imageBytes=%v copiedBytes=%v", imageSize, copied) + } + + if err := mw.WriteField("model_id", *req.ModelId); err != nil { + return nil, err + } + + if err := mw.Close(); err != nil { + return nil, err + } + + r, err := http.NewRequestWithContext(ctx, "POST", url+"/image-to-video", &buf) + if err != nil { + return nil, err + } + r.Header.Set("Content-Type", mw.FormDataContentType()) + + resp, err := sendReqWithTimeout(r, imageToVideoTimeout) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + data, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + if resp.StatusCode != 200 { + return nil, errors.New(string(data)) + } + + var tr net.TranscodeResult + if err := proto.Unmarshal(data, &tr); err != nil { + return nil, err + } + + var tdata *net.TranscodeData + switch res := tr.Result.(type) { + case *net.TranscodeResult_Error: + return nil, errors.New(res.Error) + case *net.TranscodeResult_Data: + tdata = res.Data + default: + return nil, errors.New("UnknownResponse") + } + + urls := make([]string, len(tdata.Segments)) + for i, seg := range tdata.Segments { + urls[i] = seg.Url + } + + return urls, nil +} diff --git a/server/mediaserver.go b/server/mediaserver.go index c123c578a..522d7fc14 100644 --- a/server/mediaserver.go +++ b/server/mediaserver.go @@ -184,6 +184,8 @@ func NewLivepeerServer(rtmpAddr string, lpNode *core.LivepeerNode, httpIngest bo } if lpNode.NodeType == core.BroadcasterNode && httpIngest { opts.HttpMux.HandleFunc("/live/", ls.HandlePush) + + startAIMediaServer(ls) } opts.HttpMux.HandleFunc("/recordings/", ls.HandleRecordings) return ls, nil From 7cd7913e972bae9d3c54f6ff0c6f2666d0b98012 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Sun, 21 Jan 2024 23:24:06 +0000 Subject: [PATCH 007/203] server: Impl B -> O text-to-image --- server/ai_http.go | 1 + server/ai_mediaserver.go | 34 ++++++++++++++++++++++++++++++++++ server/ai_process.go | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) diff --git a/server/ai_http.go b/server/ai_http.go index bfcf2554d..3254c55cb 100644 --- a/server/ai_http.go +++ b/server/ai_http.go @@ -51,6 +51,7 @@ func (h *lphttp) TextToImage() http.Handler { return } + w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _ = json.NewEncoder(w).Encode(resp) }) diff --git a/server/ai_mediaserver.go b/server/ai_mediaserver.go index f2a9eaefc..0d6d2d554 100644 --- a/server/ai_mediaserver.go +++ b/server/ai_mediaserver.go @@ -26,11 +26,45 @@ func startAIMediaServer(ls *LivepeerServer) error { openapi3filter.RegisterBodyDecoder("image/png", openapi3filter.FileBodyDecoder) + ls.HTTPMux.Handle("/text-to-image", oapiReqValidator(ls.TextToImage())) ls.HTTPMux.Handle("/image-to-video", oapiReqValidator(ls.ImageToVideo())) return nil } +func (ls *LivepeerServer) TextToImage() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + remoteAddr := getRemoteAddr(r) + ctx := clog.AddVal(r.Context(), clog.ClientIP, remoteAddr) + + var req worker.TextToImageJSONRequestBody + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + clog.V(common.VERBOSE).Infof(r.Context(), "Received TextToImage request prompt=%v model_id=%v", req.Prompt, *req.ModelId) + + params := aiRequestParams{ + node: ls.LivepeerNode, + } + + start := time.Now() + resp, err := processTextToImage(ctx, params, req) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + took := time.Since(start) + clog.Infof(ctx, "Processed TextToImage request prompt=%v model_id=%v took=%v", req.Prompt, *req.ModelId, took) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(resp) + }) +} + func (ls *LivepeerServer) ImageToVideo() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { remoteAddr := getRemoteAddr(r) diff --git a/server/ai_process.go b/server/ai_process.go index 498cdca59..626d847b4 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -26,6 +26,43 @@ type aiRequestParams struct { os drivers.OSSession } +func processTextToImage(ctx context.Context, params aiRequestParams, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) { + // Discover 1 orchestrator + // TODO: Discover multiple orchestrators + caps := core.NewCapabilities(core.DefaultCapabilities(), nil) + orchDesc, err := params.node.OrchestratorPool.GetOrchestrators(ctx, 1, newSuspender(), caps, common.ScoreAtLeast(0)) + if err != nil { + return nil, err + } + orchInfos := orchDesc.GetRemoteInfos() + + if len(orchInfos) == 0 { + return nil, errors.New("no orchestrators available") + } + + orchUrl := orchInfos[0].Transcoder + return submitTextToImage(ctx, orchUrl, req) +} + +func submitTextToImage(ctx context.Context, url string, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) { + client, err := worker.NewClientWithResponses(url, worker.WithHTTPClient(httpClient)) + if err != nil { + return nil, err + } + + resp, err := client.TextToImageWithResponse(ctx, req) + if err != nil { + return nil, err + } + + if resp.JSON422 != nil { + // TODO: Handle JSON422 struct + return nil, errors.New("orchestrator returned 422") + } + + return resp.JSON200, nil +} + func processImageToVideo(ctx context.Context, params aiRequestParams, req worker.ImageToVideoMultipartRequestBody) ([]string, error) { // Discover 1 orchestrator // TODO: Discover multiple orchestrators From abe1b5ade5c96148691336a5d272605e10db783c Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Sun, 21 Jan 2024 23:24:41 +0000 Subject: [PATCH 008/203] server: Set Content-Type header on B /image-to-video --- server/ai_mediaserver.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/ai_mediaserver.go b/server/ai_mediaserver.go index 0d6d2d554..aff4088e9 100644 --- a/server/ai_mediaserver.go +++ b/server/ai_mediaserver.go @@ -107,6 +107,7 @@ func (ls *LivepeerServer) ImageToVideo() http.Handler { } } + w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _ = json.NewEncoder(w).Encode(&worker.ImageResponse{Images: videos}) }) From 7ec59b362eb2e47c824a60f975d556d9d97b0264 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Mon, 22 Jan 2024 00:15:21 +0000 Subject: [PATCH 009/203] server: Impl B -> O image-to-image --- server/ai_http.go | 1 + server/ai_mediaserver.go | 40 ++++++++++++++++++++++++ server/ai_process.go | 67 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+) diff --git a/server/ai_http.go b/server/ai_http.go index 3254c55cb..b8bb5b7e7 100644 --- a/server/ai_http.go +++ b/server/ai_http.go @@ -79,6 +79,7 @@ func (h *lphttp) ImageToImage() http.Handler { return } + w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _ = json.NewEncoder(w).Encode(resp) }) diff --git a/server/ai_mediaserver.go b/server/ai_mediaserver.go index aff4088e9..a4e55ebce 100644 --- a/server/ai_mediaserver.go +++ b/server/ai_mediaserver.go @@ -27,6 +27,7 @@ func startAIMediaServer(ls *LivepeerServer) error { openapi3filter.RegisterBodyDecoder("image/png", openapi3filter.FileBodyDecoder) ls.HTTPMux.Handle("/text-to-image", oapiReqValidator(ls.TextToImage())) + ls.HTTPMux.Handle("/image-to-image", oapiReqValidator(ls.ImageToImage())) ls.HTTPMux.Handle("/image-to-video", oapiReqValidator(ls.ImageToVideo())) return nil @@ -65,6 +66,45 @@ func (ls *LivepeerServer) TextToImage() http.Handler { }) } +func (ls *LivepeerServer) ImageToImage() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + remoteAddr := getRemoteAddr(r) + ctx := clog.AddVal(r.Context(), clog.ClientIP, remoteAddr) + + multiRdr, err := r.MultipartReader() + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + var req worker.ImageToImageMultipartRequestBody + if err := runtime.BindMultipart(&req, *multiRdr); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + clog.V(common.VERBOSE).Infof(ctx, "Received ImageToImage request imageSize=%v prompt=%v model_id=%v", req.Image.FileSize(), req.Prompt, *req.ModelId) + + params := aiRequestParams{ + node: ls.LivepeerNode, + } + + start := time.Now() + resp, err := processImageToImage(ctx, params, req) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + took := time.Since(start) + clog.V(common.VERBOSE).Infof(ctx, "Processed ImageToImage request imageSize=%v prompt=%v model_id=%v took=%v", req.Image.FileSize(), req.Prompt, *req.ModelId, took) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(resp) + }) +} + func (ls *LivepeerServer) ImageToVideo() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { remoteAddr := getRemoteAddr(r) diff --git a/server/ai_process.go b/server/ai_process.go index 626d847b4..f8c760f96 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -63,6 +63,73 @@ func submitTextToImage(ctx context.Context, url string, req worker.TextToImageJS return resp.JSON200, nil } +func processImageToImage(ctx context.Context, params aiRequestParams, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { + // Discover 1 orchestrator + // TODO: Discover multiple orchestrators + caps := core.NewCapabilities(core.DefaultCapabilities(), nil) + orchDesc, err := params.node.OrchestratorPool.GetOrchestrators(ctx, 1, newSuspender(), caps, common.ScoreAtLeast(0)) + if err != nil { + return nil, err + } + orchInfos := orchDesc.GetRemoteInfos() + + if len(orchInfos) == 0 { + return nil, errors.New("no orchestrators available") + } + + orchUrl := orchInfos[0].Transcoder + return submitImageToImage(ctx, orchUrl, req) +} + +func submitImageToImage(ctx context.Context, url string, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { + var buf bytes.Buffer + mw := multipart.NewWriter(&buf) + writer, err := mw.CreateFormFile("image", req.Image.Filename()) + if err != nil { + return nil, err + } + imageSize := req.Image.FileSize() + imageRdr, err := req.Image.Reader() + if err != nil { + return nil, err + } + copied, err := io.Copy(writer, imageRdr) + if err != nil { + return nil, err + } + if copied != imageSize { + return nil, fmt.Errorf("failed to copy image to multipart request imageBytes=%v copiedBytes=%v", imageSize, copied) + } + + if err := mw.WriteField("prompt", req.Prompt); err != nil { + return nil, err + } + if err := mw.WriteField("model_id", *req.ModelId); err != nil { + return nil, err + } + + if err := mw.Close(); err != nil { + return nil, err + } + + client, err := worker.NewClientWithResponses(url, worker.WithHTTPClient(httpClient)) + if err != nil { + return nil, err + } + + resp, err := client.ImageToImageWithBodyWithResponse(ctx, mw.FormDataContentType(), &buf) + if err != nil { + return nil, err + } + + if resp.JSON422 != nil { + // TODO: Handle JSON422 struct + return nil, errors.New("orchestrator returned 422") + } + + return resp.JSON200, nil +} + func processImageToVideo(ctx context.Context, params aiRequestParams, req worker.ImageToVideoMultipartRequestBody) ([]string, error) { // Discover 1 orchestrator // TODO: Discover multiple orchestrators From 1822db5c88b233b31cb6102fe934f36437091815 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Mon, 22 Jan 2024 18:21:19 +0000 Subject: [PATCH 010/203] mod: Bump go-tools to v0.3.5 --- go.mod | 72 ++++++++-- go.sum | 317 +++++++++++++++++++++++++++++++++++++++----- server/broadcast.go | 10 +- 3 files changed, 352 insertions(+), 47 deletions(-) diff --git a/go.mod b/go.mod index dbbefed51..1ef80e0de 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 github.com/livepeer/ai-worker v0.0.0-20240117201450-dc36f66cc02a - github.com/livepeer/go-tools v0.0.0-20220805063103-76df6beb6506 + github.com/livepeer/go-tools v0.3.5 github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 github.com/livepeer/m3u8 v0.11.1 @@ -36,16 +36,16 @@ require ( ) require ( - cloud.google.com/go v0.110.0 // indirect - cloud.google.com/go/compute v1.19.1 // indirect + cloud.google.com/go v0.110.2 // indirect + cloud.google.com/go/compute v1.20.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v0.13.0 // indirect - cloud.google.com/go/storage v1.28.1 // indirect + cloud.google.com/go/iam v1.1.0 // indirect + cloud.google.com/go/storage v1.30.1 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/StackExchange/wmi v1.2.1 // indirect github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect - github.com/aws/aws-sdk-go v1.44.64 // indirect + github.com/aws/aws-sdk-go v1.44.273 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.7.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect @@ -58,7 +58,7 @@ require ( github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/deckarep/golang-set/v2 v2.1.0 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect github.com/distribution/reference v0.5.0 // indirect github.com/dlclark/regexp2 v1.7.0 // indirect github.com/docker/cli v24.0.5+incompatible // indirect @@ -75,6 +75,8 @@ require ( github.com/go-chi/chi/v5 v5.0.11 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/swag v0.22.4 // indirect @@ -85,35 +87,70 @@ require ( github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect + github.com/google/s2a-go v0.1.4 // indirect github.com/google/uuid v1.5.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect - github.com/googleapis/gax-go/v2 v2.7.1 // indirect + github.com/googleapis/gax-go/v2 v2.10.0 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/websocket v1.4.2 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/holiman/uint256 v1.2.3 // indirect github.com/invopop/yaml v0.2.0 // indirect + github.com/ipfs/bbloom v0.0.4 // indirect + github.com/ipfs/go-block-format v0.1.2 // indirect + github.com/ipfs/go-blockservice v0.5.2 // indirect + github.com/ipfs/go-cid v0.4.1 // indirect + github.com/ipfs/go-datastore v0.6.0 // indirect + github.com/ipfs/go-ipfs-blockstore v1.3.1 // indirect + github.com/ipfs/go-ipfs-ds-help v1.1.1 // indirect + github.com/ipfs/go-ipfs-exchange-interface v0.2.1 // indirect + github.com/ipfs/go-ipfs-util v0.0.3 // indirect + github.com/ipfs/go-ipld-cbor v0.0.6 // indirect + github.com/ipfs/go-ipld-format v0.4.0 // indirect + github.com/ipfs/go-ipld-legacy v0.1.1 // indirect + github.com/ipfs/go-libipfs v0.4.0 // indirect + github.com/ipfs/go-log v1.0.5 // indirect + github.com/ipfs/go-log/v2 v2.5.1 // indirect + github.com/ipfs/go-merkledag v0.10.0 // indirect + github.com/ipfs/go-metrics-interface v0.0.1 // indirect + github.com/ipfs/go-unixfs v0.4.6 // indirect + github.com/ipfs/go-verifcid v0.0.3 // indirect + github.com/ipld/go-car v0.6.0 // indirect + github.com/ipld/go-codec-dagpb v1.6.0 // indirect + github.com/ipld/go-ipld-prime v0.20.0 // indirect + github.com/jbenet/goprocess v0.1.4 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/karalabe/usb v0.0.2 // indirect github.com/klauspost/compress v1.16.7 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/sys/sequential v0.5.0 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect + github.com/multiformats/go-base32 v0.1.0 // indirect + github.com/multiformats/go-base36 v0.2.0 // indirect + github.com/multiformats/go-multibase v0.2.0 // indirect + github.com/multiformats/go-multihash v0.2.2 // indirect + github.com/multiformats/go-varint v0.0.7 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc5 // indirect github.com/opencontainers/runc v1.1.5 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/perimeterx/marshmallow v1.1.4 // indirect github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/polydawn/refmt v0.89.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect @@ -124,29 +161,38 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/sirupsen/logrus v1.9.3 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/status-im/keycard-go v0.2.0 // indirect github.com/stretchr/objx v0.5.0 // indirect github.com/supranational/blst v0.3.11 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/vincent-petithory/dataurl v1.0.0 // indirect + github.com/whyrusleeping/cbor-gen v0.0.0-20230418232409-daab9ece03a0 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.24.0 // indirect golang.org/x/crypto v0.17.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/mod v0.12.0 // indirect - golang.org/x/oauth2 v0.7.0 // indirect + golang.org/x/oauth2 v0.8.0 // indirect golang.org/x/sync v0.3.0 // indirect golang.org/x/sys v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.13.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/api v0.114.0 // indirect + google.golang.org/api v0.125.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect + google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect howett.net/plist v1.0.0 // indirect + lukechampine.com/blake3 v1.2.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect ) diff --git a/go.sum b/go.sum index 664f73a8e..e8051b3b5 100644 --- a/go.sum +++ b/go.sum @@ -13,24 +13,22 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= -cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= +cloud.google.com/go v0.110.2 h1:sdFPBr6xG9/wkBbfhmUz/JmZC7X6LavQgcrVINrKiVA= +cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY= -cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= +cloud.google.com/go/compute v1.20.0 h1:cUOcywWuowO9It2i1KX1lIb0HH7gLv6nENKuZGnlcSo= +cloud.google.com/go/compute v1.20.0/go.mod h1:kn5BhC++qUWR/AM3Dn21myV7QbgqejW04cAOrtppaQI= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= -cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= -cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= -cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= +cloud.google.com/go/iam v1.1.0 h1:67gSqaPukx7O8WLLHMa0PNs3EBGd2eE4d+psbO/CO94= +cloud.google.com/go/iam v1.1.0/go.mod h1:nxdHjaKfCr7fNYx/HJMM8LgiMugmveWlkatear5gVyk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -40,8 +38,8 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.28.1 h1:F5QDG5ChchaAVQhINh24U99OWHURqrW8OmQcGKXcbgI= -cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= +cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM= +cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= contrib.go.opencensus.io/exporter/prometheus v0.4.2 h1:sqfsYl5GIY/L570iT+l93ehxaWJs2/OwXtiWwew3oAg= contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9fpw1KeYcjrnC1J8B+JKjsZyRQ= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -72,10 +70,14 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= -github.com/aws/aws-sdk-go v1.44.64 h1:DuDZSBDkFBWW5H8q6i80RJDkBaaa/53KA6Jreqwjlqw= -github.com/aws/aws-sdk-go v1.44.64/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go v1.44.273 h1:CX8O0gK+cGrgUyv7bgJ6QQP9mQg7u5mweHdNzULH47c= +github.com/aws/aws-sdk-go v1.44.273/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -106,6 +108,11 @@ github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMn github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/errors v1.8.1 h1:A5+txlVZfOqFBDa4mGz2bUWSp0aHElvHX2bKkdbQu+Y= github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac= github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY= @@ -134,6 +141,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0= +github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis= github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -143,8 +152,8 @@ github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6 github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= @@ -172,6 +181,8 @@ github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= @@ -182,8 +193,8 @@ github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYF github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= -github.com/frankban/quicktest v1.14.2 h1:SPb1KFFmM+ybpEjPUhCCkZOM5xlovT5UbrMvWnXyBns= -github.com/frankban/quicktest v1.14.2/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= @@ -211,8 +222,11 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= @@ -238,6 +252,7 @@ github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= @@ -301,6 +316,8 @@ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= +github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -316,6 +333,8 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U= github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= +github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= @@ -324,18 +343,25 @@ github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9 github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.7.1 h1:gF4c0zjUP2H/s/hEGyLA3I0fA2ZWjzYiONAD6cvPr8A= -github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/googleapis/gax-go/v2 v2.10.0 h1:ebSgKfMxynOdxw8QQuFOKMgomqeLGPqNLQox2bo42zg= +github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= +github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw= github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= @@ -350,12 +376,89 @@ github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1: github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= +github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= +github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= +github.com/ipfs/go-bitswap v0.11.0 h1:j1WVvhDX1yhG32NTC9xfxnqycqYIlhzEzLXG/cU1HyQ= +github.com/ipfs/go-bitswap v0.11.0/go.mod h1:05aE8H3XOU+LXpTedeAS0OZpcO1WFsj5niYQH9a1Tmk= +github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJgCQF+KIgOPJY= +github.com/ipfs/go-block-format v0.0.3/go.mod h1:4LmD4ZUw0mhO+JSKdpWwrzATiEfM7WWgQ8H5l6P8MVk= +github.com/ipfs/go-block-format v0.1.2 h1:GAjkfhVx1f4YTODS6Esrj1wt2HhrtwTnhEr+DyPUaJo= +github.com/ipfs/go-block-format v0.1.2/go.mod h1:mACVcrxarQKstUU3Yf/RdwbC4DzPV6++rO2a3d+a/KE= +github.com/ipfs/go-blockservice v0.5.2 h1:in9Bc+QcXwd1apOVM7Un9t8tixPKdaHQFdLSUM1Xgk8= +github.com/ipfs/go-blockservice v0.5.2/go.mod h1:VpMblFEqG67A/H2sHKAemeH9vlURVavlysbdUI632yk= +github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= +github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= +github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= +github.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj6+M= +github.com/ipfs/go-cid v0.0.6/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= +github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= +github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= +github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= +github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk= +github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8O4Vn9YAT8= +github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= +github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= +github.com/ipfs/go-ipfs-blockstore v1.3.1 h1:cEI9ci7V0sRNivqaOr0elDsamxXFxJMMMy7PTTDQNsQ= +github.com/ipfs/go-ipfs-blockstore v1.3.1/go.mod h1:KgtZyc9fq+P2xJUiCAzbRdhhqJHvsw8u2Dlqy2MyRTE= +github.com/ipfs/go-ipfs-blocksutil v0.0.1 h1:Eh/H4pc1hsvhzsQoMEP3Bke/aW5P5rVM1IWFJMcGIPQ= +github.com/ipfs/go-ipfs-blocksutil v0.0.1/go.mod h1:Yq4M86uIOmxmGPUHv/uI7uKqZNtLb449gwKqXjIsnRk= +github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ= +github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= +github.com/ipfs/go-ipfs-ds-help v1.1.1 h1:B5UJOH52IbcfS56+Ul+sv8jnIV10lbjLF5eOO0C66Nw= +github.com/ipfs/go-ipfs-ds-help v1.1.1/go.mod h1:75vrVCkSdSFidJscs8n4W+77AtTpCIAdDGAwjitJMIo= +github.com/ipfs/go-ipfs-exchange-interface v0.2.1 h1:jMzo2VhLKSHbVe+mHNzYgs95n0+t0Q69GQ5WhRDZV/s= +github.com/ipfs/go-ipfs-exchange-interface v0.2.1/go.mod h1:MUsYn6rKbG6CTtsDp+lKJPmVt3ZrCViNyH3rfPGsZ2E= +github.com/ipfs/go-ipfs-exchange-offline v0.3.0 h1:c/Dg8GDPzixGd0MC8Jh6mjOwU57uYokgWRFidfvEkuA= +github.com/ipfs/go-ipfs-exchange-offline v0.3.0/go.mod h1:MOdJ9DChbb5u37M1IcbrRB02e++Z7521fMxqCNRrz9s= +github.com/ipfs/go-ipfs-pq v0.0.2 h1:e1vOOW6MuOwG2lqxcLA+wEn93i/9laCY8sXAw76jFOY= +github.com/ipfs/go-ipfs-pq v0.0.2/go.mod h1:LWIqQpqfRG3fNc5XsnIhz/wQ2XXGyugQwls7BgUmUfY= +github.com/ipfs/go-ipfs-routing v0.3.0 h1:9W/W3N+g+y4ZDeffSgqhgo7BsBSJwPMcyssET9OWevc= +github.com/ipfs/go-ipfs-routing v0.3.0/go.mod h1:dKqtTFIql7e1zYsEuWLyuOU+E0WJWW8JjbTPLParDWo= +github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc= +github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ= +github.com/ipfs/go-ipfs-util v0.0.3 h1:2RFdGez6bu2ZlZdI+rWfIdbQb1KudQp3VGwPtdNCmE0= +github.com/ipfs/go-ipfs-util v0.0.3/go.mod h1:LHzG1a0Ig4G+iZ26UUOMjHd+lfM84LZCrn17xAKWBvs= +github.com/ipfs/go-ipld-cbor v0.0.6 h1:pYuWHyvSpIsOOLw4Jy7NbBkCyzLDcl64Bf/LZW7eBQ0= +github.com/ipfs/go-ipld-cbor v0.0.6/go.mod h1:ssdxxaLJPXH7OjF5V4NSjBbcfh+evoR4ukuru0oPXMA= +github.com/ipfs/go-ipld-format v0.0.1/go.mod h1:kyJtbkDALmFHv3QR6et67i35QzO3S0dCDnkOJhcZkms= +github.com/ipfs/go-ipld-format v0.2.0/go.mod h1:3l3C1uKoadTPbeNfrDi+xMInYKlx2Cvg1BuydPSdzQs= +github.com/ipfs/go-ipld-format v0.4.0 h1:yqJSaJftjmjc9jEOFYlpkwOLVKv68OD27jFLlSghBlQ= +github.com/ipfs/go-ipld-format v0.4.0/go.mod h1:co/SdBE8h99968X0hViiw1MNlh6fvxxnHpvVLnH7jSM= +github.com/ipfs/go-ipld-legacy v0.1.1 h1:BvD8PEuqwBHLTKqlGFTHSwrwFOMkVESEvwIYwR2cdcc= +github.com/ipfs/go-ipld-legacy v0.1.1/go.mod h1:8AyKFCjgRPsQFf15ZQgDB8Din4DML/fOmKZkkFkrIEg= +github.com/ipfs/go-libipfs v0.4.0 h1:TkUxJGjtPnSzAgkw7VjS0/DBay3MPjmTBa4dGdUQCDE= +github.com/ipfs/go-libipfs v0.4.0/go.mod h1:XsU2cP9jBhDrXoJDe0WxikB8XcVmD3k2MEZvB3dbYu8= +github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8= +github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo= +github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g= +github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= +github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= +github.com/ipfs/go-merkledag v0.10.0 h1:IUQhj/kzTZfam4e+LnaEpoiZ9vZF6ldimVlby+6OXL4= +github.com/ipfs/go-merkledag v0.10.0/go.mod h1:zkVav8KiYlmbzUzNM6kENzkdP5+qR7+2mCwxkQ6GIj8= +github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fGD6n0jO4kdg= +github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY= +github.com/ipfs/go-peertaskqueue v0.8.0 h1:JyNO144tfu9bx6Hpo119zvbEL9iQ760FHOiJYsUjqaU= +github.com/ipfs/go-peertaskqueue v0.8.0/go.mod h1:cz8hEnnARq4Du5TGqiWKgMr/BOSQ5XOgMOh1K5YYKKM= +github.com/ipfs/go-unixfs v0.4.6 h1:4PCH8+ptflEqmD1ifrdjGu0hA/MfM1s4QlrsQb4BvJM= +github.com/ipfs/go-unixfs v0.4.6/go.mod h1:BIznJNvt/gEx/ooRMI4Us9K8+qeGO7vx1ohnbk8gjFg= +github.com/ipfs/go-verifcid v0.0.3 h1:gmRKccqhWDocCRkC+a59g5QW7uJw5bpX9HWBevXa0zs= +github.com/ipfs/go-verifcid v0.0.3/go.mod h1:gcCtGniVzelKrbk9ooUSX/pM3xlH73fZZJDzQJRvOUw= +github.com/ipld/go-car v0.6.0 h1:d5QrGLnHAxiNLHor+DKGrLdqnM0dQJh2whfSXRDq6J0= +github.com/ipld/go-car v0.6.0/go.mod h1:tBrW1XZ3L2XipLxA69RnTVGW3rve6VX4TbaTYkq8aEA= +github.com/ipld/go-codec-dagpb v1.6.0 h1:9nYazfyu9B1p3NAgfVdpRco3Fs2nFC72DqVsMj6rOcc= +github.com/ipld/go-codec-dagpb v1.6.0/go.mod h1:ANzFhfP2uMJxRBr8CE+WQWs5UsNa0pYtmKZ+agnUw9s= +github.com/ipld/go-ipld-prime v0.9.1-0.20210324083106-dc342a9917db/go.mod h1:KvBLMr4PX1gWptgkzRjVZCrLmSGcZCb/jioOQwCqZN8= +github.com/ipld/go-ipld-prime v0.20.0 h1:Ud3VwE9ClxpO2LkCYP7vWPc0Fo+dYdYzgxUJZ3uRG4g= +github.com/ipld/go-ipld-prime v0.20.0/go.mod h1:PzqZ/ZR981eKbgdr3y2DJYeD/8bgMawdGVlJDE8kK+M= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jaypipes/ghw v0.10.0 h1:UHu9UX08Py315iPojADFPOkmjTsNzHj4g4adsNKKteY= github.com/jaypipes/ghw v0.10.0/go.mod h1:jeJGbkRB2lL3/gxYzNYzEDETV1ZJ56OKr+CSeSEym+g= github.com/jaypipes/pcidb v1.0.0 h1:vtZIfkiCUE42oYbJS0TAq9XSfSmcsgo9IdxSm9qzYU8= github.com/jaypipes/pcidb v1.0.0/go.mod h1:TnYUvqhPBzCKnH34KrIX22kAeEbDCSRJ9cqLRCuNDfk= +github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= +github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o= +github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= @@ -371,6 +474,9 @@ github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= @@ -380,8 +486,13 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/koron/go-ssdp v0.0.3 h1:JivLMY45N76b4p/vsWGOKewBQu6uf39y8l+AQ7sDKx8= +github.com/koron/go-ssdp v0.0.3/go.mod h1:b2MxI6yh02pKrsyNoQUsk4+YNikaGhe4894J+Q5lDvA= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -397,10 +508,31 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+ github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= +github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= +github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= +github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= +github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= +github.com/libp2p/go-libp2p v0.23.4 h1:hWi9XHSOVFR1oDWRk7rigfyA4XNMuYL20INNybP9LP8= +github.com/libp2p/go-libp2p v0.23.4/go.mod h1:s9DEa5NLR4g+LZS+md5uGU4emjMWFiqkZr6hBTY8UxI= +github.com/libp2p/go-libp2p-asn-util v0.2.0 h1:rg3+Os8jbnO5DxkC7K/Utdi+DkY3q/d1/1q+8WeNAsw= +github.com/libp2p/go-libp2p-asn-util v0.2.0/go.mod h1:WoaWxbHKBymSN41hWSq/lGKJEca7TNm58+gGJi2WsLI= +github.com/libp2p/go-libp2p-record v0.2.0 h1:oiNUOCWno2BFuxt3my4i1frNrt7PerzB3queqa1NkQ0= +github.com/libp2p/go-libp2p-record v0.2.0/go.mod h1:I+3zMkvvg5m2OcSdoL0KPljyJyvNDFGKX7QdlpYUcwk= +github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= +github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= +github.com/libp2p/go-msgio v0.2.0 h1:W6shmB+FeynDrUVl2dgFQvzfBZcXiyqY4VmpQLu9FqU= +github.com/libp2p/go-msgio v0.2.0/go.mod h1:dBVM1gW3Jk9XqHkU4eKdGvVHdLa51hoGfll6jMJMSlY= +github.com/libp2p/go-nat v0.1.0 h1:MfVsH6DLcpa04Xr+p8hmVRG4juse0s3J8HyNWYHffXg= +github.com/libp2p/go-nat v0.1.0/go.mod h1:X7teVkwRHNInVNWQiO/tAiAVRwSr5zoRz4YSTC3uRBM= +github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4nWRE= +github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= +github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= +github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= github.com/livepeer/ai-worker v0.0.0-20240117201450-dc36f66cc02a h1:lZoWT1Y2/R0VNdtFnP1bCOXuKVpam331blamx6SCxbo= github.com/livepeer/ai-worker v0.0.0-20240117201450-dc36f66cc02a/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= -github.com/livepeer/go-tools v0.0.0-20220805063103-76df6beb6506 h1:qKon23c1RQPvL5Oya/hkImbaXNMkt6VdYtnh5jcIhoY= -github.com/livepeer/go-tools v0.0.0-20220805063103-76df6beb6506/go.mod h1:aLVS1DT0ur9kpr0IlNI4DNcm9vVjRRUjDnwuEUm0BdQ= +github.com/livepeer/go-tools v0.3.5 h1:nc94NnuTNyruCjcwKNpDHZ5ae6cxDeiabEJFhAobwmM= +github.com/livepeer/go-tools v0.3.5/go.mod h1:qs31y68b3PQPmSr8nR8l5WQiIWI623z6pqOccqebjos= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded/go.mod h1:xkDdm+akniYxVT9KW1Y2Y7Hso6aW+rZObz3nrA9yTHw= github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 h1:4oH3NqV0NvcdS44Ld3zK2tO8IUiNozIggm74yobQeZg= @@ -421,6 +553,8 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0= +github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= @@ -430,6 +564,14 @@ github.com/mattn/go-sqlite3 v1.14.18/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= +github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= +github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= +github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= +github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= @@ -456,7 +598,43 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwd github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= +github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= +github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= +github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= +github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= +github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= +github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= +github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= +github.com/multiformats/go-multiaddr v0.8.0 h1:aqjksEcqK+iD/Foe1RRFsGZh8+XFiGo7FgUCZlpv3LU= +github.com/multiformats/go-multiaddr v0.8.0/go.mod h1:Fs50eBDWvZu+l3/9S6xAE7ZYj6yhxlvaVZjakWN7xRs= +github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A= +github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk= +github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= +github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= +github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= +github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= +github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= +github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= +github.com/multiformats/go-multicodec v0.8.0 h1:evBmgkbSQux+Ds2IgfhkO38Dl2GDtRW8/Rp6YiSHX/Q= +github.com/multiformats/go-multicodec v0.8.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw= +github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= +github.com/multiformats/go-multihash v0.0.10/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= +github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= +github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= +github.com/multiformats/go-multihash v0.0.15/go.mod h1:D6aZrWNLFTV/ynMpKsNtB40mJzmCl4jb1alC0OvHiHg= +github.com/multiformats/go-multihash v0.2.2 h1:Uu7LWs/PmWby1gkj1S1DXx3zyd3aVabA4FiMKn/2tAc= +github.com/multiformats/go-multihash v0.2.2/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= +github.com/multiformats/go-multistream v0.3.3 h1:d5PZpjwRgVlbwfdTDjife7XszfZd8KYWfROYFlGcR8o= +github.com/multiformats/go-multistream v0.3.3/go.mod h1:ODRoqamLUsETKS9BNcII4gcRsJBU5VAwRIv7O39cEXg= +github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= +github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= +github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= +github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/oapi-codegen/nethttp-middleware v1.0.1 h1:ZWvwfnMU0eloHX1VEJmQscQm3741t0vCm0eSIie1NIo= @@ -484,6 +662,8 @@ github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/ github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/perimeterx/marshmallow v1.1.4 h1:pZLDH9RjlLGGorbXhcaQLhfuV0pFMNfPO55FuFkxqLw= @@ -500,6 +680,10 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/polydawn/refmt v0.0.0-20190221155625-df39d6c2d992/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= +github.com/polydawn/refmt v0.0.0-20190807091052-3d65705ee9f1/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= +github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4= +github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= @@ -538,6 +722,7 @@ github.com/rabbitmq/rabbitmq-stream-go-client v1.1.1 h1:Fji7RgmMggroffCyL0QtrhMx github.com/rabbitmq/rabbitmq-stream-go-client v1.1.1/go.mod h1:2pRPe6/8y2ZenIbnucUULMhfrPpzM90EPfjOkpsedVo= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= @@ -557,6 +742,17 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= +github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= +github.com/smartystreets/goconvey v0.0.0-20190222223459-a17d461953aa/go.mod h1:2RVY1rIf+2J2o/IM9+vPq9RzmHDSseB7FoXiSNIUsoU= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= +github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= +github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU= +github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= @@ -569,6 +765,7 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -598,6 +795,7 @@ github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95 github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8= github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= @@ -606,6 +804,15 @@ github.com/vincent-petithory/dataurl v1.0.0 h1:cXw+kPto8NLuJtlMsI152irrVw9fRDX8A github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/warpfork/go-testmark v0.11.0 h1:J6LnV8KpceDvo7spaNU4+DauH2n1x+6RaO2rJrmpQ9U= +github.com/warpfork/go-testmark v0.11.0/go.mod h1:jhEf8FVxd+F17juRubpmut64NEG6I2rgkUhlcqqXwE0= +github.com/warpfork/go-wish v0.0.0-20180510122957-5ad1f5abf436/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= +github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= +github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ= +github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= +github.com/whyrusleeping/cbor-gen v0.0.0-20200123233031-1cdf64d27158/go.mod h1:Xj/M2wWU+QdTdRbu/L/1dIZY8/Wb2K9pAhtroQuxJJI= +github.com/whyrusleeping/cbor-gen v0.0.0-20230418232409-daab9ece03a0 h1:XYEgH2nJgsrcrj32p+SAbx6T3s/6QknOXezXtz7kzbg= +github.com/whyrusleeping/cbor-gen v0.0.0-20230418232409-daab9ece03a0/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -622,16 +829,41 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= +go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -705,9 +937,11 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -717,8 +951,8 @@ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= -golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= +golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= +golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -739,6 +973,7 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -774,6 +1009,7 @@ golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -793,13 +1029,17 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -808,6 +1048,7 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -823,6 +1064,7 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -832,6 +1074,8 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -860,6 +1104,7 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= @@ -885,8 +1130,8 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.114.0 h1:1xQPji6cO2E2vLiI+C/XiFAnsn1WV3mjaEwGLhi3grE= -google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= +google.golang.org/api v0.125.0 h1:7xGvEY4fyWbhWMHf3R2/4w7L4fXyfpRGE9g6lp8+DCk= +google.golang.org/api v0.125.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -918,18 +1163,19 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 h1:9NWlQfY2ePejTmfwUH1OWwmznFa+0kKcHGPDvcPza9M= -google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk= -google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 h1:m8v1xLLLzMe1m5P+gCTF8nJB9epwZQUBERm20Oy1poQ= -google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao= +google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= +google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM= +google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -943,7 +1189,10 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.57.1 h1:upNTNqv0ES+2ZOOqACwVtS3Il8M12/+Hz41RCPzAjQg= google.golang.org/grpc v1.57.1/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -977,6 +1226,7 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -984,6 +1234,7 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -1001,6 +1252,8 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= +lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= +lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/server/broadcast.go b/server/broadcast.go index 5658f0cda..898495d02 100755 --- a/server/broadcast.go +++ b/server/broadcast.go @@ -879,7 +879,10 @@ func processSegment(ctx context.Context, cxn *rtmpConnection, seg *stream.HLSSeg ctx, cancel := clog.WithTimeout(context.Background(), ctx, recordSegmentsMaxTimeout) defer cancel() now := time.Now() - uri, err := drivers.SaveRetried(ctx, ros, name, seg.Data, map[string]string{"duration": segDurMs}, 3) + fields := &drivers.FileProperties{ + Metadata: map[string]string{"duration": segDurMs}, + } + uri, err := drivers.SaveRetried(ctx, ros, name, seg.Data, fields, 3) took := time.Since(now) if err != nil { clog.Errorf(ctx, "Error saving name=%s bytes=%d to record store err=%q", @@ -1252,7 +1255,10 @@ func downloadResults(ctx context.Context, cxn *rtmpConnection, seg *stream.HLSSe name := fmt.Sprintf("%s/%d%s", profile.Name, seg.SeqNo, ext) segDurMs := getSegDurMsString(seg) now := time.Now() - uri, err := drivers.SaveRetried(ctx, bros, name, data, map[string]string{"duration": segDurMs}, 3) + fields := &drivers.FileProperties{ + Metadata: map[string]string{"duration": segDurMs}, + } + uri, err := drivers.SaveRetried(ctx, bros, name, data, fields, 3) took := time.Since(now) if err != nil { clog.Errorf(ctx, "Error saving nonce=%d manifestID=%s name=%s to record store err=%q", nonce, cxn.mid, name, err) From 52785b27591bc80fc2f0bb4c4482ef0cb45f05d0 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Mon, 22 Jan 2024 19:58:53 +0000 Subject: [PATCH 011/203] server: Upload to OS for all AI endpoints --- server/ai_mediaserver.go | 2 ++ server/ai_process.go | 51 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/server/ai_mediaserver.go b/server/ai_mediaserver.go index a4e55ebce..710b1d089 100644 --- a/server/ai_mediaserver.go +++ b/server/ai_mediaserver.go @@ -48,6 +48,7 @@ func (ls *LivepeerServer) TextToImage() http.Handler { params := aiRequestParams{ node: ls.LivepeerNode, + os: drivers.NodeStorage.NewSession(string(core.RandomManifestID())), } start := time.Now() @@ -87,6 +88,7 @@ func (ls *LivepeerServer) ImageToImage() http.Handler { params := aiRequestParams{ node: ls.LivepeerNode, + os: drivers.NodeStorage.NewSession(string(core.RandomManifestID())), } start := time.Now() diff --git a/server/ai_process.go b/server/ai_process.go index f8c760f96..8072931c7 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -1,6 +1,7 @@ package server import ( + "bufio" "bytes" "context" "errors" @@ -41,7 +42,30 @@ func processTextToImage(ctx context.Context, params aiRequestParams, req worker. } orchUrl := orchInfos[0].Transcoder - return submitTextToImage(ctx, orchUrl, req) + resp, err := submitTextToImage(ctx, orchUrl, req) + if err != nil { + return nil, err + } + + newMedia := make([]worker.Media, len(resp.Images)) + for i, media := range resp.Images { + var data bytes.Buffer + if err := worker.ReadImageB64DataUrl(media.Url, bufio.NewWriter(&data)); err != nil { + return nil, err + } + + name := string(core.RandomManifestID()) + ".png" + newUrl, err := params.os.SaveData(ctx, name, bytes.NewReader(data.Bytes()), nil, 0) + if err != nil { + return nil, err + } + + newMedia[i] = worker.Media{Url: newUrl} + } + + resp.Images = newMedia + + return resp, nil } func submitTextToImage(ctx context.Context, url string, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) { @@ -78,7 +102,30 @@ func processImageToImage(ctx context.Context, params aiRequestParams, req worker } orchUrl := orchInfos[0].Transcoder - return submitImageToImage(ctx, orchUrl, req) + resp, err := submitImageToImage(ctx, orchUrl, req) + if err != nil { + return nil, err + } + + newMedia := make([]worker.Media, len(resp.Images)) + for i, media := range resp.Images { + var data bytes.Buffer + if err := worker.ReadImageB64DataUrl(media.Url, bufio.NewWriter(&data)); err != nil { + return nil, err + } + + name := string(core.RandomManifestID()) + ".png" + newUrl, err := params.os.SaveData(ctx, name, bytes.NewReader(data.Bytes()), nil, 0) + if err != nil { + return nil, err + } + + newMedia[i] = worker.Media{Url: newUrl} + } + + resp.Images = newMedia + + return resp, nil } func submitImageToImage(ctx context.Context, url string, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { From 332ecbd2a14c2b29fc1ff1d1855f6c851799862e Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Tue, 23 Jan 2024 01:39:03 +0000 Subject: [PATCH 012/203] mod: Bump ai-worker + go-tools --- go.mod | 16 ++++++++++++---- go.sum | 30 ++++++++++++++++++++++-------- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 1ef80e0de..a0c643bae 100644 --- a/go.mod +++ b/go.mod @@ -12,8 +12,8 @@ require ( github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.0-20240117201450-dc36f66cc02a - github.com/livepeer/go-tools v0.3.5 + github.com/livepeer/ai-worker v0.0.0-20240122195647-e9f1bf74f978 + github.com/livepeer/go-tools v0.3.6-0.20240122195736-9b20c068ccd2 github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 github.com/livepeer/m3u8 v0.11.1 @@ -67,6 +67,7 @@ require ( github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/ethereum/c-kzg-4844 v0.4.0 // indirect github.com/fatih/color v1.13.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect @@ -121,20 +122,25 @@ require ( github.com/jbenet/goprocess v0.1.4 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect github.com/karalabe/usb v0.0.2 // indirect - github.com/klauspost/compress v1.16.7 // indirect - github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/klauspost/compress v1.17.4 // indirect + github.com/klauspost/cpuid/v2 v2.2.6 // indirect github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/minio/md5-simd v1.1.2 // indirect + github.com/minio/minio-go/v7 v7.0.66 // indirect github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/sys/sequential v0.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/mr-tron/base58 v1.2.0 // indirect github.com/multiformats/go-base32 v0.1.0 // indirect @@ -158,6 +164,7 @@ require ( github.com/rabbitmq/amqp091-go v1.8.0 // indirect github.com/rabbitmq/rabbitmq-stream-go-client v1.1.1 // indirect github.com/rivo/uniseg v0.2.0 // indirect + github.com/rs/xid v1.5.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/sirupsen/logrus v1.9.3 // indirect @@ -190,6 +197,7 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect howett.net/plist v1.0.0 // indirect diff --git a/go.sum b/go.sum index e8051b3b5..bd7c623be 100644 --- a/go.sum +++ b/go.sum @@ -178,6 +178,8 @@ github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127 h1:qwcF+vdFrvPSEUDSX5R github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -471,6 +473,7 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= @@ -484,11 +487,12 @@ github.com/karalabe/usb v0.0.2 h1:M6QQBNxF+CQ8OFvxrT90BA0qBOXymndZnk5q235mFc4= github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= +github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= -github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= +github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/koron/go-ssdp v0.0.3 h1:JivLMY45N76b4p/vsWGOKewBQu6uf39y8l+AQ7sDKx8= @@ -529,10 +533,10 @@ github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4n github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/livepeer/ai-worker v0.0.0-20240117201450-dc36f66cc02a h1:lZoWT1Y2/R0VNdtFnP1bCOXuKVpam331blamx6SCxbo= -github.com/livepeer/ai-worker v0.0.0-20240117201450-dc36f66cc02a/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= -github.com/livepeer/go-tools v0.3.5 h1:nc94NnuTNyruCjcwKNpDHZ5ae6cxDeiabEJFhAobwmM= -github.com/livepeer/go-tools v0.3.5/go.mod h1:qs31y68b3PQPmSr8nR8l5WQiIWI623z6pqOccqebjos= +github.com/livepeer/ai-worker v0.0.0-20240122195647-e9f1bf74f978 h1:nfETSkDqO5QqpVV964Gb+idnJEH1cOwp3tUDpczMi4g= +github.com/livepeer/ai-worker v0.0.0-20240122195647-e9f1bf74f978/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= +github.com/livepeer/go-tools v0.3.6-0.20240122195736-9b20c068ccd2 h1:wc1oiZyJF2qMlMuInvGQ1vINZdlBtjfq8M4RLB+vA6s= +github.com/livepeer/go-tools v0.3.6-0.20240122195736-9b20c068ccd2/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded/go.mod h1:xkDdm+akniYxVT9KW1Y2Y7Hso6aW+rZObz3nrA9yTHw= github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 h1:4oH3NqV0NvcdS44Ld3zK2tO8IUiNozIggm74yobQeZg= @@ -567,6 +571,10 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfr github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= +github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= +github.com/minio/minio-go/v7 v7.0.66 h1:bnTOXOHjOqv/gcMuiVbN9o2ngRItvqE774dG9nq0Dzw= +github.com/minio/minio-go/v7 v7.0.66/go.mod h1:DHAgmyQEGdW3Cif0UooKOyrT3Vxs82zNdV6tkKhRtbs= github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= @@ -589,9 +597,11 @@ github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWK github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= 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= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= @@ -729,6 +739,8 @@ github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZV github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -1220,6 +1232,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= From 14deb6a9ede8bf168eca0907d30fb1ee877365d2 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Tue, 23 Jan 2024 01:39:54 +0000 Subject: [PATCH 013/203] cmd: Add -aiModels to load models --- cmd/livepeer/livepeer.go | 3 ++- cmd/livepeer/starter/starter.go | 19 +++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/cmd/livepeer/livepeer.go b/cmd/livepeer/livepeer.go index de1562c2c..336ae3493 100755 --- a/cmd/livepeer/livepeer.go +++ b/cmd/livepeer/livepeer.go @@ -150,7 +150,8 @@ func parseLivepeerConfig() starter.LivepeerConfig { cfg.TestTranscoder = flag.Bool("testTranscoder", *cfg.TestTranscoder, "Test Nvidia GPU transcoding at startup") // AI: - cfg.AIWorker = flag.Bool("aiworker", *cfg.AIWorker, "Set to true to run an AI worker") + cfg.AIWorker = flag.Bool("aiWorker", *cfg.AIWorker, "Set to true to run an AI worker") + cfg.AIModels = flag.String("aiModels", *cfg.AIModels, "Set models (pipeline:model_id) for AI worker to load upon initialization") // Onchain: cfg.EthAcctAddr = flag.String("ethAcctAddr", *cfg.EthAcctAddr, "Existing Eth account address. For use when multiple ETH accounts exist in the keystore directory") diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index d8d8d8bd6..a64996bea 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -95,6 +95,7 @@ type LivepeerConfig struct { Broadcaster *bool OrchSecret *string TranscodingOptions *string + AIModels *string MaxAttempts *int SelectRandWeight *float64 SelectStakeWeight *float64 @@ -184,6 +185,7 @@ func DefaultLivepeerConfig() LivepeerConfig { // AI: defaultAIWorker := false + defaultAIModels := "" // Onchain: defaultEthAcctAddr := "" @@ -271,6 +273,7 @@ func DefaultLivepeerConfig() LivepeerConfig { // AI: AIWorker: &defaultAIWorker, + AIModels: &defaultAIModels, // Onchain: EthAcctAddr: &defaultEthAcctAddr, @@ -504,11 +507,19 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { return } - warmPipelines := map[string]string{ - "text-to-image": "stabilityai/sd-turbo", + warmModels := make(map[string]string) + if *cfg.AIModels != "" { + models := strings.Split(*cfg.AIModels, ",") + for _, m := range models { + parts := strings.Split(m, ":") + pipeline := parts[0] + modelID := parts[1] + + warmModels[pipeline] = modelID + } } - for pipeline, modelID := range warmPipelines { + for pipeline, modelID := range warmModels { if err := n.AIWorker.Warm(ctx, pipeline, modelID); err != nil { glog.Errorf("Error AI worker warming %v container: %v", pipeline, err) return @@ -517,7 +528,7 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { defer func() { var stopContainerWg sync.WaitGroup - for pipeline := range warmPipelines { + for pipeline := range warmModels { stopContainerWg.Add(1) go func(pipeline string) { defer stopContainerWg.Done() From 289cb494ef038b2770879c5eac7c44ef6460f3a9 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Tue, 23 Jan 2024 17:06:34 +0000 Subject: [PATCH 014/203] server: Log oapi validation error --- server/ai_http.go | 8 +++++++- server/ai_mediaserver.go | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/server/ai_http.go b/server/ai_http.go index b8bb5b7e7..f89d0d28c 100644 --- a/server/ai_http.go +++ b/server/ai_http.go @@ -2,6 +2,7 @@ package server import ( "bytes" + "context" "encoding/json" "fmt" "net/http" @@ -24,7 +25,12 @@ func startAIServer(lp lphttp) error { } swagger.Servers = nil - oapiReqValidator := middleware.OapiRequestValidator(swagger) + opts := &middleware.Options{ + ErrorHandler: func(w http.ResponseWriter, message string, statusCode int) { + clog.Errorf(context.Background(), "oapi validation error statusCode=%v message=%v", statusCode, message) + }, + } + oapiReqValidator := middleware.OapiRequestValidatorWithOptions(swagger, opts) openapi3filter.RegisterBodyDecoder("image/png", openapi3filter.FileBodyDecoder) diff --git a/server/ai_mediaserver.go b/server/ai_mediaserver.go index 710b1d089..c6ba9d345 100644 --- a/server/ai_mediaserver.go +++ b/server/ai_mediaserver.go @@ -1,6 +1,7 @@ package server import ( + "context" "encoding/json" "net/http" "time" @@ -22,7 +23,12 @@ func startAIMediaServer(ls *LivepeerServer) error { } swagger.Servers = nil - oapiReqValidator := middleware.OapiRequestValidator(swagger) + opts := &middleware.Options{ + ErrorHandler: func(w http.ResponseWriter, message string, statusCode int) { + clog.Errorf(context.Background(), "oapi validation error statusCode=%v message=%v", statusCode, message) + }, + } + oapiReqValidator := middleware.OapiRequestValidatorWithOptions(swagger, opts) openapi3filter.RegisterBodyDecoder("image/png", openapi3filter.FileBodyDecoder) From 48f560c0ac5833319c277336f2223cc4f7fa4c1f Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Wed, 24 Jan 2024 13:00:20 +0000 Subject: [PATCH 015/203] temp disable CI tests --- .github/workflows/test.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 58897cd34..e4a25b49a 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -1,9 +1,9 @@ name: Trigger test suite on: - pull_request: - branches: - - master + # pull_request: + # branches: + # - master push: branches: - master From 24c1623af6f6e44a3224beed57a6a69842578f6f Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Wed, 24 Jan 2024 13:01:28 +0000 Subject: [PATCH 016/203] ci+docker: Use go1.21.5 --- .github/workflows/build.yaml | 4 ++-- .github/workflows/test.yaml | 2 +- docker/Dockerfile | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index cb2d590e4..9d3c029c5 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -74,7 +74,7 @@ jobs: id: go uses: actions/setup-go@v5 with: - go-version: 1.20.4 + go-version: 1.21.5 cache: true cache-dependency-path: go.sum @@ -176,7 +176,7 @@ jobs: id: go uses: actions/setup-go@v5 with: - go-version: 1.20.4 + go-version: 1.21.5 cache: true cache-dependency-path: go.sum diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index e4a25b49a..1876335c7 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -43,7 +43,7 @@ jobs: id: go uses: actions/setup-go@v5 with: - go-version: 1.20.4 + go-version: 1.21.5 cache: true cache-dependency-path: go.sum diff --git a/docker/Dockerfile b/docker/Dockerfile index 5e12c11c9..0d2901533 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -13,7 +13,7 @@ ENV GOARCH="$TARGETARCH" \ RUN apt update \ && apt install -yqq software-properties-common curl apt-transport-https lsb-release yasm \ - && curl -fsSL https://dl.google.com/go/go1.20.4.linux-${BUILDARCH}.tar.gz | tar -C /usr/local -xz \ + && curl -fsSL https://dl.google.com/go/go1.21.5.linux-${BUILDARCH}.tar.gz | tar -C /usr/local -xz \ && curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - \ && add-apt-repository "deb [arch=${BUILDARCH}] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \ && curl -fsSl https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - \ From d60b801f7ff1b3a80953bbbccf2629e838e6c3b1 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Wed, 24 Jan 2024 16:43:41 +0000 Subject: [PATCH 017/203] docker: Install zlib --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 0d2901533..120dc7476 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -19,7 +19,7 @@ RUN apt update \ && curl -fsSl https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - \ && add-apt-repository "deb [arch=${BUILDARCH}] https://apt.llvm.org/$(lsb_release -cs)/ llvm-toolchain-$(lsb_release -cs)-14 main" \ && apt update \ - && apt -yqq install clang-14 clang-tools-14 lld-14 build-essential pkg-config autoconf git python docker-ce-cli pciutils gcc-multilib libgcc-8-dev-arm64-cross gcc-mingw-w64-x86-64 + && apt -yqq install clang-14 clang-tools-14 lld-14 build-essential pkg-config autoconf git python docker-ce-cli pciutils gcc-multilib libgcc-8-dev-arm64-cross gcc-mingw-w64-x86-64 zlib1g zlib1g-dev RUN update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-14 30 \ && update-alternatives --install /usr/bin/clang clang /usr/bin/clang-14 30 \ From b49d50330f6213fb62033e9004595679ff1623cc Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Wed, 24 Jan 2024 16:44:05 +0000 Subject: [PATCH 018/203] temp disable CI arm64 builds --- .github/workflows/build.yaml | 20 ++++++++++---------- .github/workflows/docker.yaml | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 9d3c029c5..10daec16d 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -30,11 +30,11 @@ jobs: container: ubuntu:20.04 type: cpu - - GOOS: linux - GOARCH: arm64 - runner: ubuntu-20.04 - container: ubuntu:20.04 - type: cpu + # - GOOS: linux + # GOARCH: arm64 + # runner: ubuntu-20.04 + # container: ubuntu:20.04 + # type: cpu - GOOS: linux GOARCH: amd64 @@ -42,11 +42,11 @@ jobs: container: livepeerci/cuda:12.0.0-cudnn8-devel-ubuntu20.04 type: gpu - - GOOS: linux - GOARCH: arm64 - runner: ubuntu-20.04 - container: livepeerci/cuda:12.0.0-cudnn8-devel-ubuntu20.04 - type: gpu + # - GOOS: linux + # GOARCH: arm64 + # runner: ubuntu-20.04 + # container: livepeerci/cuda:12.0.0-cudnn8-devel-ubuntu20.04 + # type: gpu - GOOS: windows GOARCH: amd64 diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 6661c9dc0..8d75b7ab5 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -89,7 +89,7 @@ jobs: build-args: | BUILD_TAGS=${{ steps.build-tag.outputs.build-tags }} context: . - platforms: linux/amd64, linux/arm64 + platforms: linux/amd64 push: true tags: ${{ steps.meta.outputs.tags }} file: "docker/Dockerfile" @@ -177,7 +177,7 @@ jobs: build-args: | BUILD_TAGS=${{ steps.build-tag.outputs.build-tags }} context: . - platforms: linux/amd64, linux/arm64 + platforms: linux/amd64 push: true tags: ${{ steps.meta-builder.outputs.tags }} file: "docker/Dockerfile" From 27c8da182097992a42544a30521e0f0e95e61c15 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Wed, 24 Jan 2024 16:53:07 +0000 Subject: [PATCH 019/203] cmd: Use livepeer/ai-runner image ID --- cmd/livepeer/starter/starter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index a64996bea..c9dd611a9 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -62,7 +62,7 @@ var ( // The time to live for cached max float values for PM senders (else they will be cleaned up) in seconds smTTL = 172800 // 2 days - aiWorkerContainerImageID = "runner" + aiWorkerContainerImageID = "livepeer/ai-runner:latest" aiWorkerContainerStopTimeout = 5 * time.Second ) From 0adf0b6ac232e14a73584b9c8e1dfd90dc6c5bcf Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Wed, 24 Jan 2024 19:17:03 +0000 Subject: [PATCH 020/203] cmd: Create models dir if DNE --- cmd/livepeer/starter/starter.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index c9dd611a9..6dbf045a0 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -501,6 +501,11 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { } modelDir := path.Join(*cfg.Datadir, "models") + if err := os.MkdirAll(modelDir, 0755); err != nil { + glog.Error("Error creating models dir %v", modelDir) + return + } + n.AIWorker, err = worker.NewWorker(aiWorkerContainerImageID, *cfg.Nvidia, modelDir) if err != nil { glog.Errorf("Error starting AI worker: %v", err) From d894ee86ebee926987ade5ac7e826e6bb5bfd7e8 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Thu, 25 Jan 2024 02:01:49 +0000 Subject: [PATCH 021/203] cmd: Add -aiModelsDir flag --- cmd/livepeer/livepeer.go | 1 + cmd/livepeer/starter/starter.go | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/cmd/livepeer/livepeer.go b/cmd/livepeer/livepeer.go index 336ae3493..4b9f01ecf 100755 --- a/cmd/livepeer/livepeer.go +++ b/cmd/livepeer/livepeer.go @@ -152,6 +152,7 @@ func parseLivepeerConfig() starter.LivepeerConfig { // AI: cfg.AIWorker = flag.Bool("aiWorker", *cfg.AIWorker, "Set to true to run an AI worker") cfg.AIModels = flag.String("aiModels", *cfg.AIModels, "Set models (pipeline:model_id) for AI worker to load upon initialization") + cfg.AIModelsDir = flag.String("aiModelsDir", *cfg.AIModelsDir, "Set directory where AI model weights are stored") // Onchain: cfg.EthAcctAddr = flag.String("ethAcctAddr", *cfg.EthAcctAddr, "Existing Eth account address. For use when multiple ETH accounts exist in the keystore directory") diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index 6dbf045a0..31d184d0f 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -141,6 +141,7 @@ type LivepeerConfig struct { MetadataAmqpExchange *string MetadataPublishTimeout *time.Duration Datadir *string + AIModelsDir *string Objectstore *string Recordstore *string FVfailGsBucket *string @@ -186,6 +187,7 @@ func DefaultLivepeerConfig() LivepeerConfig { // AI: defaultAIWorker := false defaultAIModels := "" + defaultAIModelsDir := "" // Onchain: defaultEthAcctAddr := "" @@ -272,8 +274,9 @@ func DefaultLivepeerConfig() LivepeerConfig { TestTranscoder: &defaultTestTranscoder, // AI: - AIWorker: &defaultAIWorker, - AIModels: &defaultAIModels, + AIWorker: &defaultAIWorker, + AIModels: &defaultAIModels, + AIModelsDir: &defaultAIModelsDir, // Onchain: EthAcctAddr: &defaultEthAcctAddr, @@ -500,13 +503,17 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { return } - modelDir := path.Join(*cfg.Datadir, "models") - if err := os.MkdirAll(modelDir, 0755); err != nil { - glog.Error("Error creating models dir %v", modelDir) + modelsDir := *cfg.AIModelsDir + if modelsDir == "" { + modelsDir = path.Join(*cfg.Datadir, "models") + } + + if err := os.MkdirAll(modelsDir, 0755); err != nil { + glog.Error("Error creating models dir %v", modelsDir) return } - n.AIWorker, err = worker.NewWorker(aiWorkerContainerImageID, *cfg.Nvidia, modelDir) + n.AIWorker, err = worker.NewWorker(aiWorkerContainerImageID, *cfg.Nvidia, modelsDir) if err != nil { glog.Errorf("Error starting AI worker: %v", err) return From d1af6e281565cca182923ed21e942bacaf6799fa Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Thu, 25 Jan 2024 18:26:10 +0000 Subject: [PATCH 022/203] server: Log HTTP errors for AI endpoints --- server/ai_http.go | 18 +++++++++--------- server/ai_mediaserver.go | 16 ++++++++-------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/server/ai_http.go b/server/ai_http.go index f89d0d28c..01bd0585f 100644 --- a/server/ai_http.go +++ b/server/ai_http.go @@ -45,7 +45,7 @@ func (h *lphttp) TextToImage() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var req worker.TextToImageJSONRequestBody if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) + respondWithError(w, err.Error(), http.StatusBadRequest) return } @@ -53,7 +53,7 @@ func (h *lphttp) TextToImage() http.Handler { resp, err := h.orchestrator.TextToImage(r.Context(), req) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + respondWithError(w, err.Error(), http.StatusInternalServerError) return } @@ -67,13 +67,13 @@ func (h *lphttp) ImageToImage() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { multiRdr, err := r.MultipartReader() if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) + respondWithError(w, err.Error(), http.StatusBadRequest) return } var req worker.ImageToImageMultipartRequestBody if err := runtime.BindMultipart(&req, *multiRdr); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + respondWithError(w, err.Error(), http.StatusInternalServerError) return } @@ -98,13 +98,13 @@ func (h *lphttp) ImageToVideo() http.Handler { multiRdr, err := r.MultipartReader() if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) + respondWithError(w, err.Error(), http.StatusBadRequest) return } var req worker.ImageToVideoMultipartRequestBody if err := runtime.BindMultipart(&req, *multiRdr); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + respondWithError(w, err.Error(), http.StatusInternalServerError) return } @@ -112,13 +112,13 @@ func (h *lphttp) ImageToVideo() http.Handler { results, err := h.orchestrator.ImageToVideo(ctx, req) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + respondWithError(w, err.Error(), http.StatusInternalServerError) return } // TODO: Handle more than one video if len(results) != 1 { - http.Error(w, "failed to return results", http.StatusInternalServerError) + respondWithError(w, "failed to return results", http.StatusInternalServerError) return } @@ -157,7 +157,7 @@ func (h *lphttp) ImageToVideo() http.Handler { buf, err := proto.Marshal(tr) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + respondWithError(w, err.Error(), http.StatusInternalServerError) return } diff --git a/server/ai_mediaserver.go b/server/ai_mediaserver.go index c6ba9d345..3ffe07e05 100644 --- a/server/ai_mediaserver.go +++ b/server/ai_mediaserver.go @@ -46,7 +46,7 @@ func (ls *LivepeerServer) TextToImage() http.Handler { var req worker.TextToImageJSONRequestBody if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) + respondWithError(w, err.Error(), http.StatusBadRequest) return } @@ -60,7 +60,7 @@ func (ls *LivepeerServer) TextToImage() http.Handler { start := time.Now() resp, err := processTextToImage(ctx, params, req) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + respondWithError(w, err.Error(), http.StatusInternalServerError) return } @@ -80,13 +80,13 @@ func (ls *LivepeerServer) ImageToImage() http.Handler { multiRdr, err := r.MultipartReader() if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) + respondWithError(w, err.Error(), http.StatusBadRequest) return } var req worker.ImageToImageMultipartRequestBody if err := runtime.BindMultipart(&req, *multiRdr); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + respondWithError(w, err.Error(), http.StatusInternalServerError) return } @@ -100,7 +100,7 @@ func (ls *LivepeerServer) ImageToImage() http.Handler { start := time.Now() resp, err := processImageToImage(ctx, params, req) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + respondWithError(w, err.Error(), http.StatusInternalServerError) return } @@ -120,13 +120,13 @@ func (ls *LivepeerServer) ImageToVideo() http.Handler { multiRdr, err := r.MultipartReader() if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) + respondWithError(w, err.Error(), http.StatusBadRequest) return } var req worker.ImageToVideoMultipartRequestBody if err := runtime.BindMultipart(&req, *multiRdr); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + respondWithError(w, err.Error(), http.StatusInternalServerError) return } @@ -140,7 +140,7 @@ func (ls *LivepeerServer) ImageToVideo() http.Handler { start := time.Now() urls, err := processImageToVideo(ctx, params, req) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + respondWithError(w, err.Error(), http.StatusInternalServerError) return } From 1759f35f1c6e27de05a0785cf21a4a90a160d8e0 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Thu, 25 Jan 2024 19:38:47 +0000 Subject: [PATCH 023/203] server: Add AI process logs for O --- server/ai_http.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/server/ai_http.go b/server/ai_http.go index 01bd0585f..37a222bfc 100644 --- a/server/ai_http.go +++ b/server/ai_http.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "net/http" + "time" "github.com/getkin/kin-openapi/openapi3filter" "github.com/golang/protobuf/proto" @@ -43,6 +44,9 @@ func startAIServer(lp lphttp) error { func (h *lphttp) TextToImage() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + remoteAddr := getRemoteAddr(r) + ctx := clog.AddVal(r.Context(), clog.ClientIP, remoteAddr) + var req worker.TextToImageJSONRequestBody if err := json.NewDecoder(r.Body).Decode(&req); err != nil { respondWithError(w, err.Error(), http.StatusBadRequest) @@ -51,12 +55,16 @@ func (h *lphttp) TextToImage() http.Handler { clog.V(common.VERBOSE).Infof(r.Context(), "Received TextToImage request prompt=%v model_id=%v", req.Prompt, *req.ModelId) + start := time.Now() resp, err := h.orchestrator.TextToImage(r.Context(), req) if err != nil { respondWithError(w, err.Error(), http.StatusInternalServerError) return } + took := time.Since(start) + clog.Infof(ctx, "Processed TextToImage request prompt=%v model_id=%v took=%v", req.Prompt, *req.ModelId, took) + w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _ = json.NewEncoder(w).Encode(resp) @@ -65,6 +73,9 @@ func (h *lphttp) TextToImage() http.Handler { func (h *lphttp) ImageToImage() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + remoteAddr := getRemoteAddr(r) + ctx := clog.AddVal(r.Context(), clog.ClientIP, remoteAddr) + multiRdr, err := r.MultipartReader() if err != nil { respondWithError(w, err.Error(), http.StatusBadRequest) @@ -79,12 +90,16 @@ func (h *lphttp) ImageToImage() http.Handler { clog.V(common.VERBOSE).Infof(r.Context(), "Received ImageToImage request imageSize=%v prompt=%v model_id=%v", req.Image.FileSize(), req.Prompt, *req.ModelId) + start := time.Now() resp, err := h.orchestrator.ImageToImage(r.Context(), req) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } + took := time.Since(start) + clog.Infof(ctx, "Processed ImageToImage request imageSize=%v prompt=%v model_id=%v took=%v", req.Image.FileSize(), req.Prompt, *req.ModelId, took) + w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _ = json.NewEncoder(w).Encode(resp) @@ -110,6 +125,7 @@ func (h *lphttp) ImageToVideo() http.Handler { clog.V(common.VERBOSE).Infof(ctx, "Received ImageToVideo request imageSize=%v model_id=%v", req.Image.FileSize(), *req.ModelId) + start := time.Now() results, err := h.orchestrator.ImageToVideo(ctx, req) if err != nil { respondWithError(w, err.Error(), http.StatusInternalServerError) @@ -122,6 +138,9 @@ func (h *lphttp) ImageToVideo() http.Handler { return } + took := time.Since(start) + clog.Infof(ctx, "Processed ImageToVideo request imageSize=%v model_id=%v took=%v", req.Image.FileSize(), *req.ModelId, took) + res := results[0] // Assume only single rendition right now From e089c102cd226ddaed3daa1543524f4e5ef0f8f2 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Thu, 25 Jan 2024 19:41:50 +0000 Subject: [PATCH 024/203] mod+cmd: Bump ai-worker --- cmd/livepeer/starter/starter.go | 26 +++++++++++--------------- core/ai.go | 2 +- go.mod | 2 +- go.sum | 4 ++-- 4 files changed, 15 insertions(+), 19 deletions(-) diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index 31d184d0f..89bb69139 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -18,7 +18,6 @@ import ( "path/filepath" "strconv" "strings" - "sync" "time" ethcommon "github.com/ethereum/go-ethereum/common" @@ -502,6 +501,11 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { glog.Error("-nvidia required when using -aiworker") return } + gpus, err := common.ParseAccelDevices(*cfg.Nvidia, ffmpeg.Nvidia) + if err != nil { + glog.Errorf("Error parsing -nvidia for devices: %v", err) + return + } modelsDir := *cfg.AIModelsDir if modelsDir == "" { @@ -513,7 +517,7 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { return } - n.AIWorker, err = worker.NewWorker(aiWorkerContainerImageID, *cfg.Nvidia, modelsDir) + n.AIWorker, err = worker.NewWorker(aiWorkerContainerImageID, gpus, modelsDir) if err != nil { glog.Errorf("Error starting AI worker: %v", err) return @@ -539,21 +543,13 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { } defer func() { - var stopContainerWg sync.WaitGroup - for pipeline := range warmModels { - stopContainerWg.Add(1) - go func(pipeline string) { - defer stopContainerWg.Done() - ctx, cancel := context.WithTimeout(context.Background(), aiWorkerContainerStopTimeout) - defer cancel() - if err := n.AIWorker.Stop(ctx, pipeline); err != nil { - glog.Errorf("Error AI worker stopping %v container: %v", pipeline, err) - return - } - }(pipeline) + ctx, cancel := context.WithTimeout(context.Background(), aiWorkerContainerStopTimeout) + defer cancel() + if err := n.AIWorker.Stop(ctx); err != nil { + glog.Errorf("Error stopping AI worker containers: %v", err) + return } - stopContainerWg.Wait() glog.Infof("Stopped AI worker containers") }() } diff --git a/core/ai.go b/core/ai.go index 9d874f4f2..264cd7d37 100644 --- a/core/ai.go +++ b/core/ai.go @@ -11,5 +11,5 @@ type AI interface { ImageToImage(context.Context, worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) ImageToVideo(context.Context, worker.ImageToVideoMultipartRequestBody) (*worker.VideoResponse, error) Warm(context.Context, string, string) error - Stop(context.Context, string) error + Stop(context.Context) error } diff --git a/go.mod b/go.mod index a0c643bae..89b0f591a 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.0-20240122195647-e9f1bf74f978 + github.com/livepeer/ai-worker v0.0.0-20240125194003-92b0d6bfe6ac github.com/livepeer/go-tools v0.3.6-0.20240122195736-9b20c068ccd2 github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 diff --git a/go.sum b/go.sum index bd7c623be..df987c8f9 100644 --- a/go.sum +++ b/go.sum @@ -533,8 +533,8 @@ github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4n github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/livepeer/ai-worker v0.0.0-20240122195647-e9f1bf74f978 h1:nfETSkDqO5QqpVV964Gb+idnJEH1cOwp3tUDpczMi4g= -github.com/livepeer/ai-worker v0.0.0-20240122195647-e9f1bf74f978/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= +github.com/livepeer/ai-worker v0.0.0-20240125194003-92b0d6bfe6ac h1:T6o8Z8+hzE8khv6IT8vqf8KiNmvbKgUXfED3TOBfE9I= +github.com/livepeer/ai-worker v0.0.0-20240125194003-92b0d6bfe6ac/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= github.com/livepeer/go-tools v0.3.6-0.20240122195736-9b20c068ccd2 h1:wc1oiZyJF2qMlMuInvGQ1vINZdlBtjfq8M4RLB+vA6s= github.com/livepeer/go-tools v0.3.6-0.20240122195736-9b20c068ccd2/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= From 857ee5f984eeb59f41a14a837b494404274fcc4b Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Fri, 26 Jan 2024 22:43:42 +0000 Subject: [PATCH 025/203] server: Check JSON200 from O --- server/ai_process.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/server/ai_process.go b/server/ai_process.go index 8072931c7..f67faaa63 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -84,6 +84,10 @@ func submitTextToImage(ctx context.Context, url string, req worker.TextToImageJS return nil, errors.New("orchestrator returned 422") } + if resp.JSON200 == nil { + return nil, errors.New("orchestrator did not return a response") + } + return resp.JSON200, nil } @@ -174,6 +178,10 @@ func submitImageToImage(ctx context.Context, url string, req worker.ImageToImage return nil, errors.New("orchestrator returned 422") } + if resp.JSON200 == nil { + return nil, errors.New("orchestrator did not return a response") + } + return resp.JSON200, nil } From 3fcc300b42a0e758ebe63f2599a5556a5bd95390 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Fri, 26 Jan 2024 22:43:55 +0000 Subject: [PATCH 026/203] server: Temp disable oapi validation --- server/ai_http.go | 3 +++ server/ai_mediaserver.go | 3 +++ 2 files changed, 6 insertions(+) diff --git a/server/ai_http.go b/server/ai_http.go index 37a222bfc..55e2837ee 100644 --- a/server/ai_http.go +++ b/server/ai_http.go @@ -27,6 +27,9 @@ func startAIServer(lp lphttp) error { swagger.Servers = nil opts := &middleware.Options{ + Options: openapi3filter.Options{ + ExcludeRequestBody: true, + }, ErrorHandler: func(w http.ResponseWriter, message string, statusCode int) { clog.Errorf(context.Background(), "oapi validation error statusCode=%v message=%v", statusCode, message) }, diff --git a/server/ai_mediaserver.go b/server/ai_mediaserver.go index 3ffe07e05..cb3df23c7 100644 --- a/server/ai_mediaserver.go +++ b/server/ai_mediaserver.go @@ -24,6 +24,9 @@ func startAIMediaServer(ls *LivepeerServer) error { swagger.Servers = nil opts := &middleware.Options{ + Options: openapi3filter.Options{ + ExcludeRequestBody: true, + }, ErrorHandler: func(w http.ResponseWriter, message string, statusCode int) { clog.Errorf(context.Background(), "oapi validation error statusCode=%v message=%v", statusCode, message) }, From d6d42615222a1c4b0816a5c894a1c8b4e778b2f6 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Fri, 26 Jan 2024 22:45:40 +0000 Subject: [PATCH 027/203] mod: Bump ai-worker --- go.mod | 2 +- go.sum | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 89b0f591a..eefefa567 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.0-20240125194003-92b0d6bfe6ac + github.com/livepeer/ai-worker v0.0.0-20240126224247-72596a17c8d3 github.com/livepeer/go-tools v0.3.6-0.20240122195736-9b20c068ccd2 github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 diff --git a/go.sum b/go.sum index df987c8f9..33d3404f5 100644 --- a/go.sum +++ b/go.sum @@ -535,6 +535,10 @@ github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+O github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= github.com/livepeer/ai-worker v0.0.0-20240125194003-92b0d6bfe6ac h1:T6o8Z8+hzE8khv6IT8vqf8KiNmvbKgUXfED3TOBfE9I= github.com/livepeer/ai-worker v0.0.0-20240125194003-92b0d6bfe6ac/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= +github.com/livepeer/ai-worker v0.0.0-20240126151305-f5ba038b95cf h1:xhbmrYtBFmMy4O135yM5yA5sBAHVpWs799bFvgZHaCA= +github.com/livepeer/ai-worker v0.0.0-20240126151305-f5ba038b95cf/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= +github.com/livepeer/ai-worker v0.0.0-20240126224247-72596a17c8d3 h1:f1TV81ip1OBJnE4daSJtXxPqfYlzMg7e/c0vC2GJe18= +github.com/livepeer/ai-worker v0.0.0-20240126224247-72596a17c8d3/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= github.com/livepeer/go-tools v0.3.6-0.20240122195736-9b20c068ccd2 h1:wc1oiZyJF2qMlMuInvGQ1vINZdlBtjfq8M4RLB+vA6s= github.com/livepeer/go-tools v0.3.6-0.20240122195736-9b20c068ccd2/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= From 19da59d76a3e160f74f06b2185087250fca90c30 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Tue, 30 Jan 2024 20:56:47 +0000 Subject: [PATCH 028/203] mod+server: Prefer: respond-async for image-to-video --- go.mod | 2 +- go.sum | 8 +- server/ai_mediaserver.go | 170 +++++++++++++++++++++++++++++++++++---- server/ai_process.go | 12 ++- 4 files changed, 166 insertions(+), 26 deletions(-) diff --git a/go.mod b/go.mod index eefefa567..a0a233829 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 github.com/livepeer/ai-worker v0.0.0-20240126224247-72596a17c8d3 - github.com/livepeer/go-tools v0.3.6-0.20240122195736-9b20c068ccd2 + github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 github.com/livepeer/m3u8 v0.11.1 diff --git a/go.sum b/go.sum index 33d3404f5..b8e32e079 100644 --- a/go.sum +++ b/go.sum @@ -533,14 +533,10 @@ github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4n github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/livepeer/ai-worker v0.0.0-20240125194003-92b0d6bfe6ac h1:T6o8Z8+hzE8khv6IT8vqf8KiNmvbKgUXfED3TOBfE9I= -github.com/livepeer/ai-worker v0.0.0-20240125194003-92b0d6bfe6ac/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= -github.com/livepeer/ai-worker v0.0.0-20240126151305-f5ba038b95cf h1:xhbmrYtBFmMy4O135yM5yA5sBAHVpWs799bFvgZHaCA= -github.com/livepeer/ai-worker v0.0.0-20240126151305-f5ba038b95cf/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= github.com/livepeer/ai-worker v0.0.0-20240126224247-72596a17c8d3 h1:f1TV81ip1OBJnE4daSJtXxPqfYlzMg7e/c0vC2GJe18= github.com/livepeer/ai-worker v0.0.0-20240126224247-72596a17c8d3/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= -github.com/livepeer/go-tools v0.3.6-0.20240122195736-9b20c068ccd2 h1:wc1oiZyJF2qMlMuInvGQ1vINZdlBtjfq8M4RLB+vA6s= -github.com/livepeer/go-tools v0.3.6-0.20240122195736-9b20c068ccd2/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= +github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= +github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded/go.mod h1:xkDdm+akniYxVT9KW1Y2Y7Hso6aW+rZObz3nrA9yTHw= github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 h1:4oH3NqV0NvcdS44Ld3zK2tO8IUiNozIggm74yobQeZg= diff --git a/server/ai_mediaserver.go b/server/ai_mediaserver.go index cb3df23c7..c60b64b98 100644 --- a/server/ai_mediaserver.go +++ b/server/ai_mediaserver.go @@ -1,6 +1,7 @@ package server import ( + "bytes" "context" "encoding/json" "net/http" @@ -16,6 +17,31 @@ import ( "github.com/oapi-codegen/runtime" ) +type ImageToVideoResponseAsync struct { + RequestID string `json:"request_id"` +} + +type ImageToVideoResultRequest struct { + RequestID string `json:"request_id"` +} + +type ImageToVideoResultResponse struct { + Result *ImageToVideoResult `json:"result,omitempty"` + Status ImageToVideoStatus `json:"status"` +} + +type ImageToVideoResult struct { + *worker.ImageResponse + Error error `json:"error,omitempty"` +} + +type ImageToVideoStatus string + +const ( + Processing ImageToVideoStatus = "processing" + Complete ImageToVideoStatus = "complete" +) + func startAIMediaServer(ls *LivepeerServer) error { swagger, err := worker.GetSwagger() if err != nil { @@ -38,6 +64,7 @@ func startAIMediaServer(ls *LivepeerServer) error { ls.HTTPMux.Handle("/text-to-image", oapiReqValidator(ls.TextToImage())) ls.HTTPMux.Handle("/image-to-image", oapiReqValidator(ls.ImageToImage())) ls.HTTPMux.Handle("/image-to-video", oapiReqValidator(ls.ImageToVideo())) + ls.HTTPMux.Handle("/image-to-video/result", ls.ImageToVideoResult()) return nil } @@ -133,33 +160,146 @@ func (ls *LivepeerServer) ImageToVideo() http.Handler { return } - clog.V(common.VERBOSE).Infof(ctx, "Received ImageToVideo request imageSize=%v model_id=%v", req.Image.FileSize(), *req.ModelId) + var async bool + prefer := r.Header.Get("Prefer") + if prefer == "respond-async" { + async = true + } + + requestID := string(core.RandomManifestID()) + + clog.V(common.VERBOSE).Infof(ctx, "Received ImageToVideo request request_id=%v imageSize=%v model_id=%v async=%v", requestID, req.Image.FileSize(), *req.ModelId, async) params := aiRequestParams{ node: ls.LivepeerNode, - os: drivers.NodeStorage.NewSession(string(core.RandomManifestID())), + os: drivers.NodeStorage.NewSession(requestID), } - start := time.Now() - urls, err := processImageToVideo(ctx, params, req) - if err != nil { + resultCh := make(chan ImageToVideoResult) + go func(ctx context.Context) { + start := time.Now() + + if async { + // Use new context instead of request context for background processing + ctx = context.Background() + + var data bytes.Buffer + if err := json.NewEncoder(&data).Encode(req); err != nil { + clog.Errorf(ctx, "Error JSON encoding ImageToVideo request request_id=%v err=%v", requestID, err) + return + } + + path, err := params.os.SaveData(ctx, "request.json", bytes.NewReader(data.Bytes()), nil, 0) + if err != nil { + clog.Errorf(ctx, "Error saving ImageToVideo request to object store request_id=%v err=%v", requestID, err) + return + } + + clog.Infof(ctx, "Saved ImageToVideo request request_id=%v path=%v", requestID, path) + } + + resp, err := processImageToVideo(ctx, params, req) + if err != nil { + clog.Errorf(ctx, "Error processing ImageToVideo request request_id=%v err=%v", requestID, err) + } else { + took := time.Since(start) + clog.Infof(ctx, "Processed ImageToVideo request request_id=%v imageSize=%v model_id=%v took=%v", requestID, req.Image.FileSize(), *req.ModelId, took) + } + + result := ImageToVideoResult{ + ImageResponse: resp, + Error: err, + } + + // If async = false, then we expect a receiver on the channel and return early + // If async = true, then there is no receiver so we move on to save the result in the object store + select { + case resultCh <- result: + return + default: + } + + var data bytes.Buffer + if err := json.NewEncoder(&data).Encode(result); err != nil { + clog.Errorf(ctx, "Error JSON encoding ImageToVideo result request_id=%v err=%v", requestID, err) + return + } + + path, err := params.os.SaveData(ctx, "result.json", bytes.NewReader(data.Bytes()), nil, 0) + if err != nil { + clog.Errorf(ctx, "Error saving ImageToVideo result to object store request_id=%v err=%v", requestID, err) + return + } + + clog.Infof(ctx, "Saved ImageToVideo result request_id=%v path=%v", requestID, path) + }(ctx) + + w.Header().Set("Content-Type", "application/json") + + if async { + w.WriteHeader(http.StatusAccepted) + resp := &ImageToVideoResponseAsync{ + RequestID: requestID, + } + _ = json.NewEncoder(w).Encode(resp) + return + } + + result := <-resultCh + if result.Error != nil { respondWithError(w, err.Error(), http.StatusInternalServerError) return } - took := time.Since(start) - clog.Infof(ctx, "Processed ImageToVideo request imageSize=%v model_id=%v took=%v", req.Image.FileSize(), *req.ModelId, took) + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(result.ImageResponse) + }) +} - // HACK: Re-use worker.ImageResponse to return results - videos := make([]worker.Media, len(urls)) - for i, url := range urls { - videos[i] = worker.Media{ - Url: url, - } +func (ls *LivepeerServer) ImageToVideoResult() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + remoteAddr := getRemoteAddr(r) + ctx := clog.AddVal(r.Context(), clog.ClientIP, remoteAddr) + + var req ImageToVideoResultRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + respondWithError(w, err.Error(), http.StatusBadRequest) + return + } + + clog.V(common.VERBOSE).Infof(ctx, "Received ImageToVideoResult request request_id=%v", req.RequestID) + + sess := drivers.NodeStorage.NewSession(req.RequestID) + + _, err := sess.ReadData(ctx, "request.json") + if err != nil { + clog.Errorf(ctx, "Error fetching ImageToVideo request err=%v", err) + respondWithError(w, "invalid request", http.StatusBadRequest) + return + } + + resp := ImageToVideoResultResponse{ + Status: Processing, + } + + reader, err := sess.ReadData(ctx, "result.json") + if err != nil { + // TODO: Distinguish between error reading data vs. file DNE + // Right now we assume that this file will exist when processing is done even + // if an error was encountered + w.WriteHeader(http.StatusAccepted) + _ = json.NewEncoder(w).Encode(resp) + return + } + + resp.Status = Complete + + if err := json.NewDecoder(reader.Body).Decode(&resp.Result); err != nil { + respondWithError(w, err.Error(), http.StatusInternalServerError) + return } - w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - _ = json.NewEncoder(w).Encode(&worker.ImageResponse{Images: videos}) + _ = json.NewEncoder(w).Encode(resp) }) } diff --git a/server/ai_process.go b/server/ai_process.go index f67faaa63..38a58bef8 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -185,7 +185,7 @@ func submitImageToImage(ctx context.Context, url string, req worker.ImageToImage return resp.JSON200, nil } -func processImageToVideo(ctx context.Context, params aiRequestParams, req worker.ImageToVideoMultipartRequestBody) ([]string, error) { +func processImageToVideo(ctx context.Context, params aiRequestParams, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { // Discover 1 orchestrator // TODO: Discover multiple orchestrators caps := core.NewCapabilities(core.DefaultCapabilities(), nil) @@ -205,7 +205,8 @@ func processImageToVideo(ctx context.Context, params aiRequestParams, req worker return nil, err } - newUrls := make([]string, len(urls)) + // HACK: Re-use worker.ImageResponse to return results + videos := make([]worker.Media, len(urls)) for i, url := range urls { data, err := downloadSeg(ctx, url) if err != nil { @@ -218,10 +219,13 @@ func processImageToVideo(ctx context.Context, params aiRequestParams, req worker return nil, err } - newUrls[i] = newUrl + videos[i] = worker.Media{ + Url: newUrl, + } } - return newUrls, nil + resp := &worker.ImageResponse{Images: videos} + return resp, nil } func submitImageToVideo(ctx context.Context, url string, req worker.ImageToVideoMultipartRequestBody) ([]string, error) { From 7d765d62d719a33058dcee81f79d4c6f03b30d04 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Wed, 31 Jan 2024 20:52:10 +0000 Subject: [PATCH 029/203] server: Fix error check in ImageToVideo --- server/ai_mediaserver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/ai_mediaserver.go b/server/ai_mediaserver.go index c60b64b98..c58dfa075 100644 --- a/server/ai_mediaserver.go +++ b/server/ai_mediaserver.go @@ -247,7 +247,7 @@ func (ls *LivepeerServer) ImageToVideo() http.Handler { result := <-resultCh if result.Error != nil { - respondWithError(w, err.Error(), http.StatusInternalServerError) + respondWithError(w, result.Error.Error(), http.StatusInternalServerError) return } From 9985d5f279941b2d0f4b9d644bc544b5935b0508 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Thu, 1 Feb 2024 02:10:13 +0000 Subject: [PATCH 030/203] server: Fix JSON for ImageToVideoResult.Error --- server/ai_mediaserver.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/server/ai_mediaserver.go b/server/ai_mediaserver.go index c58dfa075..33bc1102d 100644 --- a/server/ai_mediaserver.go +++ b/server/ai_mediaserver.go @@ -32,7 +32,8 @@ type ImageToVideoResultResponse struct { type ImageToVideoResult struct { *worker.ImageResponse - Error error `json:"error,omitempty"` + ErrorMsg string `json:"error,omitempty"` + Error error `json:"-"` } type ImageToVideoStatus string @@ -199,18 +200,21 @@ func (ls *LivepeerServer) ImageToVideo() http.Handler { } resp, err := processImageToVideo(ctx, params, req) + + result := ImageToVideoResult{ + ImageResponse: resp, + Error: err, + } + if err != nil { clog.Errorf(ctx, "Error processing ImageToVideo request request_id=%v err=%v", requestID, err) + + result.ErrorMsg = err.Error() } else { took := time.Since(start) clog.Infof(ctx, "Processed ImageToVideo request request_id=%v imageSize=%v model_id=%v took=%v", requestID, req.Image.FileSize(), *req.ModelId, took) } - result := ImageToVideoResult{ - ImageResponse: resp, - Error: err, - } - // If async = false, then we expect a receiver on the channel and return early // If async = true, then there is no receiver so we move on to save the result in the object store select { From 2ecaaab3f732f3015ea4a62b3524370db23da95c Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Thu, 1 Feb 2024 02:13:38 +0000 Subject: [PATCH 031/203] mod: Bump ai-worker --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index a0a233829..3c4126336 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.0-20240126224247-72596a17c8d3 + github.com/livepeer/ai-worker v0.0.0-20240201020921-29ff04ceacad github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 diff --git a/go.sum b/go.sum index b8e32e079..7a5117ef4 100644 --- a/go.sum +++ b/go.sum @@ -535,6 +535,8 @@ github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+O github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= github.com/livepeer/ai-worker v0.0.0-20240126224247-72596a17c8d3 h1:f1TV81ip1OBJnE4daSJtXxPqfYlzMg7e/c0vC2GJe18= github.com/livepeer/ai-worker v0.0.0-20240126224247-72596a17c8d3/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= +github.com/livepeer/ai-worker v0.0.0-20240201020921-29ff04ceacad h1:gRfTlvHljDQ/eeFNsZF91zkWYs6Se5Br2ARksW+y9O8= +github.com/livepeer/ai-worker v0.0.0-20240201020921-29ff04ceacad/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= From dafefd1b59c1368fb800fb121af4f66e04c97551 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Thu, 1 Feb 2024 19:25:01 +0000 Subject: [PATCH 032/203] server: Add error struct for AI endpoints --- server/ai_mediaserver.go | 141 ++++++++++++++++++--------------------- server/handlers.go | 38 +++++++++++ 2 files changed, 104 insertions(+), 75 deletions(-) diff --git a/server/ai_mediaserver.go b/server/ai_mediaserver.go index 33bc1102d..ab114867d 100644 --- a/server/ai_mediaserver.go +++ b/server/ai_mediaserver.go @@ -32,8 +32,7 @@ type ImageToVideoResultResponse struct { type ImageToVideoResult struct { *worker.ImageResponse - ErrorMsg string `json:"error,omitempty"` - Error error `json:"-"` + Error *APIError `json:"error,omitempty"` } type ImageToVideoStatus string @@ -74,10 +73,12 @@ func (ls *LivepeerServer) TextToImage() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { remoteAddr := getRemoteAddr(r) ctx := clog.AddVal(r.Context(), clog.ClientIP, remoteAddr) + requestID := string(core.RandomManifestID()) + ctx = clog.AddVal(ctx, "request_id", requestID) var req worker.TextToImageJSONRequestBody if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - respondWithError(w, err.Error(), http.StatusBadRequest) + respondJsonError(ctx, w, &APIError{Message: err.Error()}, http.StatusBadRequest) return } @@ -85,13 +86,13 @@ func (ls *LivepeerServer) TextToImage() http.Handler { params := aiRequestParams{ node: ls.LivepeerNode, - os: drivers.NodeStorage.NewSession(string(core.RandomManifestID())), + os: drivers.NodeStorage.NewSession(requestID), } start := time.Now() resp, err := processTextToImage(ctx, params, req) if err != nil { - respondWithError(w, err.Error(), http.StatusInternalServerError) + respondJsonError(ctx, w, err, http.StatusInternalServerError) return } @@ -108,16 +109,18 @@ func (ls *LivepeerServer) ImageToImage() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { remoteAddr := getRemoteAddr(r) ctx := clog.AddVal(r.Context(), clog.ClientIP, remoteAddr) + requestID := string(core.RandomManifestID()) + ctx = clog.AddVal(ctx, "request_id", requestID) multiRdr, err := r.MultipartReader() if err != nil { - respondWithError(w, err.Error(), http.StatusBadRequest) + respondJsonError(ctx, w, &APIError{Message: err.Error()}, http.StatusBadRequest) return } var req worker.ImageToImageMultipartRequestBody if err := runtime.BindMultipart(&req, *multiRdr); err != nil { - respondWithError(w, err.Error(), http.StatusInternalServerError) + respondJsonError(ctx, w, &APIError{Message: err.Error()}, http.StatusBadRequest) return } @@ -131,7 +134,7 @@ func (ls *LivepeerServer) ImageToImage() http.Handler { start := time.Now() resp, err := processImageToImage(ctx, params, req) if err != nil { - respondWithError(w, err.Error(), http.StatusInternalServerError) + respondJsonError(ctx, w, err, http.StatusInternalServerError) return } @@ -148,16 +151,18 @@ func (ls *LivepeerServer) ImageToVideo() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { remoteAddr := getRemoteAddr(r) ctx := clog.AddVal(r.Context(), clog.ClientIP, remoteAddr) + requestID := string(core.RandomManifestID()) + ctx = clog.AddVal(ctx, "request_id", requestID) multiRdr, err := r.MultipartReader() if err != nil { - respondWithError(w, err.Error(), http.StatusBadRequest) + respondJsonError(ctx, w, &APIError{Message: err.Error()}, http.StatusBadRequest) return } var req worker.ImageToVideoMultipartRequestBody if err := runtime.BindMultipart(&req, *multiRdr); err != nil { - respondWithError(w, err.Error(), http.StatusInternalServerError) + respondJsonError(ctx, w, &APIError{Message: err.Error()}, http.StatusBadRequest) return } @@ -167,96 +172,81 @@ func (ls *LivepeerServer) ImageToVideo() http.Handler { async = true } - requestID := string(core.RandomManifestID()) - - clog.V(common.VERBOSE).Infof(ctx, "Received ImageToVideo request request_id=%v imageSize=%v model_id=%v async=%v", requestID, req.Image.FileSize(), *req.ModelId, async) + clog.V(common.VERBOSE).Infof(ctx, "Received ImageToVideo request imageSize=%v model_id=%v async=%v", req.Image.FileSize(), *req.ModelId, async) params := aiRequestParams{ node: ls.LivepeerNode, os: drivers.NodeStorage.NewSession(requestID), } - resultCh := make(chan ImageToVideoResult) - go func(ctx context.Context) { + if !async { start := time.Now() - if async { - // Use new context instead of request context for background processing - ctx = context.Background() + resp, err := processImageToVideo(ctx, params, req) + if err != nil { + respondJsonError(ctx, w, err, http.StatusInternalServerError) + return + } - var data bytes.Buffer - if err := json.NewEncoder(&data).Encode(req); err != nil { - clog.Errorf(ctx, "Error JSON encoding ImageToVideo request request_id=%v err=%v", requestID, err) - return - } + took := time.Since(start) + clog.Infof(ctx, "Processed ImageToVideo request imageSize=%v model_id=%v took=%v", req.Image.FileSize(), *req.ModelId, took) - path, err := params.os.SaveData(ctx, "request.json", bytes.NewReader(data.Bytes()), nil, 0) - if err != nil { - clog.Errorf(ctx, "Error saving ImageToVideo request to object store request_id=%v err=%v", requestID, err) - return - } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(resp) + return + } - clog.Infof(ctx, "Saved ImageToVideo request request_id=%v path=%v", requestID, path) - } + var data bytes.Buffer + if err := json.NewEncoder(&data).Encode(req); err != nil { + respondJsonError(ctx, w, err, http.StatusInternalServerError) + return + } - resp, err := processImageToVideo(ctx, params, req) + path, err := params.os.SaveData(ctx, "request.json", bytes.NewReader(data.Bytes()), nil, 0) + if err != nil { + respondJsonError(ctx, w, err, http.StatusInternalServerError) + return + } - result := ImageToVideoResult{ - ImageResponse: resp, - Error: err, - } + clog.Infof(ctx, "Saved ImageToVideo request path=%v", requestID, path) + cctx := clog.Clone(context.Background(), ctx) + go func(ctx context.Context) { + start := time.Now() + + var data bytes.Buffer + resp, err := processImageToVideo(ctx, params, req) if err != nil { - clog.Errorf(ctx, "Error processing ImageToVideo request request_id=%v err=%v", requestID, err) + clog.Errorf(ctx, "Error processing ImageToVideo request err=%v", err) - result.ErrorMsg = err.Error() + handleAPIError(ctx, &data, err, http.StatusInternalServerError) } else { took := time.Since(start) - clog.Infof(ctx, "Processed ImageToVideo request request_id=%v imageSize=%v model_id=%v took=%v", requestID, req.Image.FileSize(), *req.ModelId, took) - } + clog.Infof(ctx, "Processed ImageToVideo request imageSize=%v model_id=%v took=%v", req.Image.FileSize(), *req.ModelId, took) - // If async = false, then we expect a receiver on the channel and return early - // If async = true, then there is no receiver so we move on to save the result in the object store - select { - case resultCh <- result: - return - default: - } - - var data bytes.Buffer - if err := json.NewEncoder(&data).Encode(result); err != nil { - clog.Errorf(ctx, "Error JSON encoding ImageToVideo result request_id=%v err=%v", requestID, err) - return + if err := json.NewEncoder(&data).Encode(resp); err != nil { + clog.Errorf(ctx, "Error JSON encoding ImageToVideo response err=%v", err) + return + } } path, err := params.os.SaveData(ctx, "result.json", bytes.NewReader(data.Bytes()), nil, 0) if err != nil { - clog.Errorf(ctx, "Error saving ImageToVideo result to object store request_id=%v err=%v", requestID, err) + clog.Errorf(ctx, "Error saving ImageToVideo result to object store err=%v", err) return } - clog.Infof(ctx, "Saved ImageToVideo result request_id=%v path=%v", requestID, path) - }(ctx) + clog.Infof(ctx, "Saved ImageToVideo result path=%v", path) + }(cctx) - w.Header().Set("Content-Type", "application/json") - - if async { - w.WriteHeader(http.StatusAccepted) - resp := &ImageToVideoResponseAsync{ - RequestID: requestID, - } - _ = json.NewEncoder(w).Encode(resp) - return + resp := &ImageToVideoResponseAsync{ + RequestID: requestID, } - result := <-resultCh - if result.Error != nil { - respondWithError(w, result.Error.Error(), http.StatusInternalServerError) - return - } - - w.WriteHeader(http.StatusOK) - _ = json.NewEncoder(w).Encode(result.ImageResponse) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + _ = json.NewEncoder(w).Encode(resp) }) } @@ -267,18 +257,19 @@ func (ls *LivepeerServer) ImageToVideoResult() http.Handler { var req ImageToVideoResultRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - respondWithError(w, err.Error(), http.StatusBadRequest) + respondJsonError(ctx, w, &APIError{Message: err.Error()}, http.StatusBadRequest) return } + ctx = clog.AddVal(ctx, "request_id", req.RequestID) + clog.V(common.VERBOSE).Infof(ctx, "Received ImageToVideoResult request request_id=%v", req.RequestID) sess := drivers.NodeStorage.NewSession(req.RequestID) _, err := sess.ReadData(ctx, "request.json") if err != nil { - clog.Errorf(ctx, "Error fetching ImageToVideo request err=%v", err) - respondWithError(w, "invalid request", http.StatusBadRequest) + respondJsonError(ctx, w, &APIError{Message: "invalid request ID"}, http.StatusBadRequest) return } @@ -299,7 +290,7 @@ func (ls *LivepeerServer) ImageToVideoResult() http.Handler { resp.Status = Complete if err := json.NewDecoder(reader.Body).Decode(&resp.Result); err != nil { - respondWithError(w, err.Error(), http.StatusInternalServerError) + respondJsonError(ctx, w, err, http.StatusInternalServerError) return } diff --git a/server/handlers.go b/server/handlers.go index 7461065fc..fd4328a96 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -1,8 +1,10 @@ package server import ( + "context" "encoding/json" "fmt" + "io" "math/big" "net/http" "net/url" @@ -16,6 +18,7 @@ import ( ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/signer/core/apitypes" "github.com/golang/glog" + "github.com/livepeer/go-livepeer/clog" "github.com/livepeer/go-livepeer/common" "github.com/livepeer/go-livepeer/core" "github.com/livepeer/go-livepeer/eth" @@ -1462,6 +1465,41 @@ func respondJsonOk(w http.ResponseWriter, msg []byte) { respondOk(w, msg) } +type APIErrorResponse struct { + Error error `json:"error"` +} + +type APIError struct { + Message string `json:"message"` +} + +func (err *APIError) Error() string { return err.Message } + +func handleAPIError(ctx context.Context, w io.Writer, err error, code int) { + clog.Errorf(ctx, "Error with API code=%v err=%v", code, err) + + var apiErr *APIError + if !errors.As(err, &apiErr) || code == http.StatusInternalServerError { + // If the caller did not pass an APIError assume that the + // message should not be passed to API consumer + apiErr = &APIError{ + Message: "Internal Server Error", + } + } + + resp := &APIErrorResponse{Error: apiErr} + if err := json.NewEncoder(w).Encode(resp); err != nil { + clog.Errorf(ctx, "Error with API JSON encoding err=%v", err) + } +} + +func respondJsonError(ctx context.Context, w http.ResponseWriter, err error, code int) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(code) + + handleAPIError(ctx, w, err, code) +} + func respond500(w http.ResponseWriter, errMsg string) { respondWithError(w, errMsg, http.StatusInternalServerError) } From c35735ccc4010a6c9f8c5733c36b4412ddfcb9d4 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Thu, 1 Feb 2024 21:26:41 +0000 Subject: [PATCH 033/203] server: Add backoff for AI requests --- server/ai_mediaserver.go | 31 ++++++++++++++----- server/ai_process.go | 65 ++++++++++++++++++++++++++++++++++------ server/handlers.go | 13 ++++---- 3 files changed, 86 insertions(+), 23 deletions(-) diff --git a/server/ai_mediaserver.go b/server/ai_mediaserver.go index ab114867d..3f610c63e 100644 --- a/server/ai_mediaserver.go +++ b/server/ai_mediaserver.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/json" + "errors" "net/http" "time" @@ -78,7 +79,7 @@ func (ls *LivepeerServer) TextToImage() http.Handler { var req worker.TextToImageJSONRequestBody if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - respondJsonError(ctx, w, &APIError{Message: err.Error()}, http.StatusBadRequest) + respondJsonError(ctx, w, err, http.StatusBadRequest) return } @@ -92,6 +93,11 @@ func (ls *LivepeerServer) TextToImage() http.Handler { start := time.Now() resp, err := processTextToImage(ctx, params, req) if err != nil { + var e *ServiceUnavailableError + if errors.As(err, &e) { + respondJsonError(ctx, w, err, http.StatusServiceUnavailable) + return + } respondJsonError(ctx, w, err, http.StatusInternalServerError) return } @@ -114,13 +120,13 @@ func (ls *LivepeerServer) ImageToImage() http.Handler { multiRdr, err := r.MultipartReader() if err != nil { - respondJsonError(ctx, w, &APIError{Message: err.Error()}, http.StatusBadRequest) + respondJsonError(ctx, w, err, http.StatusBadRequest) return } var req worker.ImageToImageMultipartRequestBody if err := runtime.BindMultipart(&req, *multiRdr); err != nil { - respondJsonError(ctx, w, &APIError{Message: err.Error()}, http.StatusBadRequest) + respondJsonError(ctx, w, err, http.StatusBadRequest) return } @@ -134,6 +140,11 @@ func (ls *LivepeerServer) ImageToImage() http.Handler { start := time.Now() resp, err := processImageToImage(ctx, params, req) if err != nil { + var e *ServiceUnavailableError + if errors.As(err, &e) { + respondJsonError(ctx, w, err, http.StatusServiceUnavailable) + return + } respondJsonError(ctx, w, err, http.StatusInternalServerError) return } @@ -156,13 +167,13 @@ func (ls *LivepeerServer) ImageToVideo() http.Handler { multiRdr, err := r.MultipartReader() if err != nil { - respondJsonError(ctx, w, &APIError{Message: err.Error()}, http.StatusBadRequest) + respondJsonError(ctx, w, err, http.StatusBadRequest) return } var req worker.ImageToVideoMultipartRequestBody if err := runtime.BindMultipart(&req, *multiRdr); err != nil { - respondJsonError(ctx, w, &APIError{Message: err.Error()}, http.StatusBadRequest) + respondJsonError(ctx, w, err, http.StatusBadRequest) return } @@ -184,6 +195,12 @@ func (ls *LivepeerServer) ImageToVideo() http.Handler { resp, err := processImageToVideo(ctx, params, req) if err != nil { + var e *ServiceUnavailableError + if errors.As(err, &e) { + respondJsonError(ctx, w, err, http.StatusServiceUnavailable) + return + } + respondJsonError(ctx, w, err, http.StatusInternalServerError) return } @@ -257,7 +274,7 @@ func (ls *LivepeerServer) ImageToVideoResult() http.Handler { var req ImageToVideoResultRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - respondJsonError(ctx, w, &APIError{Message: err.Error()}, http.StatusBadRequest) + respondJsonError(ctx, w, err, http.StatusBadRequest) return } @@ -269,7 +286,7 @@ func (ls *LivepeerServer) ImageToVideoResult() http.Handler { _, err := sess.ReadData(ctx, "request.json") if err != nil { - respondJsonError(ctx, w, &APIError{Message: "invalid request ID"}, http.StatusBadRequest) + respondJsonError(ctx, w, errors.New("invalid request ID"), http.StatusBadRequest) return } diff --git a/server/ai_process.go b/server/ai_process.go index 38a58bef8..046180db0 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -12,8 +12,10 @@ import ( "path/filepath" "time" + "github.com/cenkalti/backoff" "github.com/golang/protobuf/proto" "github.com/livepeer/ai-worker/worker" + "github.com/livepeer/go-livepeer/clog" "github.com/livepeer/go-livepeer/common" "github.com/livepeer/go-livepeer/core" "github.com/livepeer/go-livepeer/net" @@ -21,6 +23,18 @@ import ( ) const imageToVideoTimeout = 5 * time.Minute +const textToImageRetryBackoff = 10 * time.Second +const imageToImageRetryBackoff = 10 * time.Second +const imageToVideoRetryBackoff = 1 * time.Minute +const maxProcessingRetries = 4 + +type ServiceUnavailableError struct { + err error +} + +func (e *ServiceUnavailableError) Error() string { + return e.err.Error() +} type aiRequestParams struct { node *core.LivepeerNode @@ -42,9 +56,20 @@ func processTextToImage(ctx context.Context, params aiRequestParams, req worker. } orchUrl := orchInfos[0].Transcoder - resp, err := submitTextToImage(ctx, orchUrl, req) - if err != nil { - return nil, err + + var resp *worker.ImageResponse + op := func() error { + var err error + resp, err = submitTextToImage(ctx, orchUrl, req) + return err + } + notify := func(err error, dur time.Duration) { + clog.Infof(ctx, "Error submitting TextToImage request err=%v retrying after dur=%v", err, dur) + } + + b := backoff.WithMaxRetries(backoff.NewConstantBackOff(textToImageRetryBackoff), maxProcessingRetries) + if err := backoff.RetryNotify(op, b, notify); err != nil { + return nil, &ServiceUnavailableError{err: err} } newMedia := make([]worker.Media, len(resp.Images)) @@ -106,9 +131,20 @@ func processImageToImage(ctx context.Context, params aiRequestParams, req worker } orchUrl := orchInfos[0].Transcoder - resp, err := submitImageToImage(ctx, orchUrl, req) - if err != nil { - return nil, err + + var resp *worker.ImageResponse + op := func() error { + var err error + resp, err = submitImageToImage(ctx, orchUrl, req) + return err + } + notify := func(err error, dur time.Duration) { + clog.Infof(ctx, "Error submitting ImageToImage request err=%v retrying after dur=%v", err, dur) + } + + b := backoff.WithMaxRetries(backoff.NewConstantBackOff(imageToImageRetryBackoff), maxProcessingRetries) + if err := backoff.RetryNotify(op, b, notify); err != nil { + return nil, &ServiceUnavailableError{err: err} } newMedia := make([]worker.Media, len(resp.Images)) @@ -200,9 +236,20 @@ func processImageToVideo(ctx context.Context, params aiRequestParams, req worker } orchUrl := orchInfos[0].Transcoder - urls, err := submitImageToVideo(ctx, orchUrl, req) - if err != nil { - return nil, err + + var urls []string + op := func() error { + var err error + urls, err = submitImageToVideo(ctx, orchUrl, req) + return err + } + notify := func(err error, dur time.Duration) { + clog.Infof(ctx, "Error submitting ImageToVideo request err=%v retrying after dur=%v", err, dur) + } + + b := backoff.WithMaxRetries(backoff.NewConstantBackOff(imageToVideoRetryBackoff), maxProcessingRetries) + if err := backoff.RetryNotify(op, b, notify); err != nil { + return nil, &ServiceUnavailableError{err: err} } // HACK: Re-use worker.ImageResponse to return results diff --git a/server/handlers.go b/server/handlers.go index fd4328a96..23992a165 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -1478,13 +1478,12 @@ func (err *APIError) Error() string { return err.Message } func handleAPIError(ctx context.Context, w io.Writer, err error, code int) { clog.Errorf(ctx, "Error with API code=%v err=%v", code, err) - var apiErr *APIError - if !errors.As(err, &apiErr) || code == http.StatusInternalServerError { - // If the caller did not pass an APIError assume that the - // message should not be passed to API consumer - apiErr = &APIError{ - Message: "Internal Server Error", - } + apiErr := &APIError{Message: err.Error()} + + if code == http.StatusInternalServerError { + apiErr.Message = "Internal Server Error" + } else if code == http.StatusServiceUnavailable { + apiErr.Message = "Service Unavailable Error" } resp := &APIErrorResponse{Error: apiErr} From 9521fcb88e2774118809c1f17e00f592879a8bdd Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Mon, 5 Feb 2024 16:43:28 +0000 Subject: [PATCH 034/203] mod: Bump ai-worker --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 3c4126336..fabfce769 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.0-20240201020921-29ff04ceacad + github.com/livepeer/ai-worker v0.0.0-20240202211855-823caeaa265f github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 diff --git a/go.sum b/go.sum index 7a5117ef4..4cf5ab75c 100644 --- a/go.sum +++ b/go.sum @@ -537,6 +537,8 @@ github.com/livepeer/ai-worker v0.0.0-20240126224247-72596a17c8d3 h1:f1TV81ip1OBJ github.com/livepeer/ai-worker v0.0.0-20240126224247-72596a17c8d3/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= github.com/livepeer/ai-worker v0.0.0-20240201020921-29ff04ceacad h1:gRfTlvHljDQ/eeFNsZF91zkWYs6Se5Br2ARksW+y9O8= github.com/livepeer/ai-worker v0.0.0-20240201020921-29ff04ceacad/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= +github.com/livepeer/ai-worker v0.0.0-20240202211855-823caeaa265f h1:8owDNiBfN0j62k5YQKm5sk24HQquyKvviUKoB3ok8mM= +github.com/livepeer/ai-worker v0.0.0-20240202211855-823caeaa265f/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= From 9f5792a120e48e721e34594b0416c4c0edeb58f9 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Mon, 5 Feb 2024 16:43:42 +0000 Subject: [PATCH 035/203] server: Read errors from O for AI requests --- server/ai_http.go | 2 +- server/ai_process.go | 17 +++++------------ 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/server/ai_http.go b/server/ai_http.go index 55e2837ee..57c59bb36 100644 --- a/server/ai_http.go +++ b/server/ai_http.go @@ -96,7 +96,7 @@ func (h *lphttp) ImageToImage() http.Handler { start := time.Now() resp, err := h.orchestrator.ImageToImage(r.Context(), req) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + respondWithError(w, err.Error(), http.StatusInternalServerError) return } diff --git a/server/ai_process.go b/server/ai_process.go index 046180db0..cb6ace715 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -10,6 +10,7 @@ import ( "mime/multipart" "net/http" "path/filepath" + "strings" "time" "github.com/cenkalti/backoff" @@ -104,13 +105,9 @@ func submitTextToImage(ctx context.Context, url string, req worker.TextToImageJS return nil, err } - if resp.JSON422 != nil { - // TODO: Handle JSON422 struct - return nil, errors.New("orchestrator returned 422") - } - if resp.JSON200 == nil { - return nil, errors.New("orchestrator did not return a response") + // TODO: Replace trim newline with better error spec from O + return nil, errors.New(strings.TrimSuffix(string(resp.Body), "\n")) } return resp.JSON200, nil @@ -209,13 +206,9 @@ func submitImageToImage(ctx context.Context, url string, req worker.ImageToImage return nil, err } - if resp.JSON422 != nil { - // TODO: Handle JSON422 struct - return nil, errors.New("orchestrator returned 422") - } - if resp.JSON200 == nil { - return nil, errors.New("orchestrator did not return a response") + // TODO: Replace trim newline with better error spec from O + return nil, errors.New(strings.TrimSuffix(string(resp.Body), "\n")) } return resp.JSON200, nil From 924c712dfcb3115103c8d1fbda6c071e912b01eb Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Mon, 5 Feb 2024 18:52:59 +0000 Subject: [PATCH 036/203] server: Use multipart writer helpers for AI reqs --- go.mod | 2 +- go.sum | 2 ++ server/ai_process.go | 51 ++------------------------------------------ 3 files changed, 5 insertions(+), 50 deletions(-) diff --git a/go.mod b/go.mod index fabfce769..6bb76ba70 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.0-20240202211855-823caeaa265f + github.com/livepeer/ai-worker v0.0.0-20240205185039-5c4895915580 github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 diff --git a/go.sum b/go.sum index 4cf5ab75c..6b077971b 100644 --- a/go.sum +++ b/go.sum @@ -539,6 +539,8 @@ github.com/livepeer/ai-worker v0.0.0-20240201020921-29ff04ceacad h1:gRfTlvHljDQ/ github.com/livepeer/ai-worker v0.0.0-20240201020921-29ff04ceacad/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= github.com/livepeer/ai-worker v0.0.0-20240202211855-823caeaa265f h1:8owDNiBfN0j62k5YQKm5sk24HQquyKvviUKoB3ok8mM= github.com/livepeer/ai-worker v0.0.0-20240202211855-823caeaa265f/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= +github.com/livepeer/ai-worker v0.0.0-20240205185039-5c4895915580 h1:7ACCHUpeJsoWADgST/nWfGD0LVRSXFcYG6FTGvzUGn4= +github.com/livepeer/ai-worker v0.0.0-20240205185039-5c4895915580/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= diff --git a/server/ai_process.go b/server/ai_process.go index cb6ace715..14fbc46ef 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -5,9 +5,7 @@ import ( "bytes" "context" "errors" - "fmt" "io" - "mime/multipart" "net/http" "path/filepath" "strings" @@ -167,34 +165,10 @@ func processImageToImage(ctx context.Context, params aiRequestParams, req worker func submitImageToImage(ctx context.Context, url string, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { var buf bytes.Buffer - mw := multipart.NewWriter(&buf) - writer, err := mw.CreateFormFile("image", req.Image.Filename()) + mw, err := worker.NewImageToImageMultipartWriter(&buf, req) if err != nil { return nil, err } - imageSize := req.Image.FileSize() - imageRdr, err := req.Image.Reader() - if err != nil { - return nil, err - } - copied, err := io.Copy(writer, imageRdr) - if err != nil { - return nil, err - } - if copied != imageSize { - return nil, fmt.Errorf("failed to copy image to multipart request imageBytes=%v copiedBytes=%v", imageSize, copied) - } - - if err := mw.WriteField("prompt", req.Prompt); err != nil { - return nil, err - } - if err := mw.WriteField("model_id", *req.ModelId); err != nil { - return nil, err - } - - if err := mw.Close(); err != nil { - return nil, err - } client, err := worker.NewClientWithResponses(url, worker.WithHTTPClient(httpClient)) if err != nil { @@ -270,31 +244,10 @@ func processImageToVideo(ctx context.Context, params aiRequestParams, req worker func submitImageToVideo(ctx context.Context, url string, req worker.ImageToVideoMultipartRequestBody) ([]string, error) { var buf bytes.Buffer - mw := multipart.NewWriter(&buf) - writer, err := mw.CreateFormFile("image", req.Image.Filename()) - if err != nil { - return nil, err - } - imageSize := req.Image.FileSize() - imageRdr, err := req.Image.Reader() - if err != nil { - return nil, err - } - copied, err := io.Copy(writer, imageRdr) + mw, err := worker.NewImageToVideoMultipartWriter(&buf, req) if err != nil { return nil, err } - if copied != imageSize { - return nil, fmt.Errorf("failed to copy image to multipart request imageBytes=%v copiedBytes=%v", imageSize, copied) - } - - if err := mw.WriteField("model_id", *req.ModelId); err != nil { - return nil, err - } - - if err := mw.Close(); err != nil { - return nil, err - } r, err := http.NewRequestWithContext(ctx, "POST", url+"/image-to-video", &buf) if err != nil { From 209ec4841649e2e01cc1e51a99f5a61b3f838eee Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Thu, 8 Feb 2024 18:36:26 +0000 Subject: [PATCH 037/203] mod+server: Add seed in AI responses --- core/orchestrator.go | 26 ++++++++++++++++---- go.mod | 2 +- go.sum | 4 ++++ server/ai_http.go | 57 ++++---------------------------------------- server/ai_process.go | 54 +++++++++++++++-------------------------- server/rpc.go | 2 +- 6 files changed, 50 insertions(+), 95 deletions(-) diff --git a/core/orchestrator.go b/core/orchestrator.go index 185c343f8..f6f316625 100644 --- a/core/orchestrator.go +++ b/core/orchestrator.go @@ -113,7 +113,7 @@ func (orch *orchestrator) ImageToImage(ctx context.Context, req worker.ImageToIm return orch.node.imageToImage(ctx, req) } -func (orch *orchestrator) ImageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) ([]*TranscodeResult, error) { +func (orch *orchestrator) ImageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { return orch.node.imageToVideo(ctx, req) } @@ -892,7 +892,7 @@ func (n *LivepeerNode) imageToImage(ctx context.Context, req worker.ImageToImage return n.AIWorker.ImageToImage(ctx, req) } -func (n *LivepeerNode) imageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) ([]*TranscodeResult, error) { +func (n *LivepeerNode) imageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { // We might support generating more than one video in the future (i.e. multiple input images/prompts) numVideos := 1 @@ -911,8 +911,9 @@ func (n *LivepeerNode) imageToVideo(ctx context.Context, req worker.ImageToVideo clog.V(common.DEBUG).Infof(ctx, "Generating frames took=%v", took) sessionID := string(RandomManifestID()) + // HACK: Re-use worker.ImageResponse to return results // Transcode frames into segments. - results := make([]*TranscodeResult, len(resp.Frames)) + videos := make([]worker.Media, len(resp.Frames)) for i, batch := range resp.Frames { // Create slice of frame urls for a batch urls := make([]string, len(batch)) @@ -926,10 +927,25 @@ func (n *LivepeerNode) imageToVideo(ctx context.Context, req worker.ImageToVideo return nil, res.Err } - results[i] = res + // Assume only single rendition right now + seg := res.TranscodeData.Segments[0] + name := fmt.Sprintf("%v.mp4", RandomManifestID()) + segData := bytes.NewReader(seg.Data) + uri, err := res.OS.SaveData(ctx, name, segData, nil, 0) + if err != nil { + return nil, err + } + + videos[i] = worker.Media{ + Url: uri, + } + + if len(batch) > 0 { + videos[i].Seed = batch[0].Seed + } } - return results, nil + return &worker.ImageResponse{Images: videos}, nil } func (rtm *RemoteTranscoderManager) transcoderResults(tcID int64, res *RemoteTranscoderResult) { diff --git a/go.mod b/go.mod index 6bb76ba70..93aafd3b0 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.0-20240205185039-5c4895915580 + github.com/livepeer/ai-worker v0.0.0-20240208183357-408784061f26 github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 diff --git a/go.sum b/go.sum index 6b077971b..2c89897c2 100644 --- a/go.sum +++ b/go.sum @@ -541,6 +541,10 @@ github.com/livepeer/ai-worker v0.0.0-20240202211855-823caeaa265f h1:8owDNiBfN0j6 github.com/livepeer/ai-worker v0.0.0-20240202211855-823caeaa265f/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= github.com/livepeer/ai-worker v0.0.0-20240205185039-5c4895915580 h1:7ACCHUpeJsoWADgST/nWfGD0LVRSXFcYG6FTGvzUGn4= github.com/livepeer/ai-worker v0.0.0-20240205185039-5c4895915580/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= +github.com/livepeer/ai-worker v0.0.0-20240208153040-7c92507e2a40 h1:vVbuu5wqrzq6M6Rlutk0eZv6qZ/kO2OrqQv5n6yt57s= +github.com/livepeer/ai-worker v0.0.0-20240208153040-7c92507e2a40/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= +github.com/livepeer/ai-worker v0.0.0-20240208183357-408784061f26 h1:N15ODU1wgHrjdLtB3vKQrhq2+Y6aatcA6kk21bLvCeM= +github.com/livepeer/ai-worker v0.0.0-20240208183357-408784061f26/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= diff --git a/server/ai_http.go b/server/ai_http.go index 57c59bb36..c6343a454 100644 --- a/server/ai_http.go +++ b/server/ai_http.go @@ -1,20 +1,15 @@ package server import ( - "bytes" "context" "encoding/json" - "fmt" "net/http" "time" "github.com/getkin/kin-openapi/openapi3filter" - "github.com/golang/protobuf/proto" "github.com/livepeer/ai-worker/worker" "github.com/livepeer/go-livepeer/clog" "github.com/livepeer/go-livepeer/common" - "github.com/livepeer/go-livepeer/core" - "github.com/livepeer/go-livepeer/net" middleware "github.com/oapi-codegen/nethttp-middleware" "github.com/oapi-codegen/runtime" ) @@ -56,7 +51,7 @@ func (h *lphttp) TextToImage() http.Handler { return } - clog.V(common.VERBOSE).Infof(r.Context(), "Received TextToImage request prompt=%v model_id=%v", req.Prompt, *req.ModelId) + clog.V(common.VERBOSE).Infof(ctx, "Received TextToImage request prompt=%v model_id=%v", req.Prompt, *req.ModelId) start := time.Now() resp, err := h.orchestrator.TextToImage(r.Context(), req) @@ -129,61 +124,17 @@ func (h *lphttp) ImageToVideo() http.Handler { clog.V(common.VERBOSE).Infof(ctx, "Received ImageToVideo request imageSize=%v model_id=%v", req.Image.FileSize(), *req.ModelId) start := time.Now() - results, err := h.orchestrator.ImageToVideo(ctx, req) + resp, err := h.orchestrator.ImageToVideo(ctx, req) if err != nil { respondWithError(w, err.Error(), http.StatusInternalServerError) return } - // TODO: Handle more than one video - if len(results) != 1 { - respondWithError(w, "failed to return results", http.StatusInternalServerError) - return - } - took := time.Since(start) clog.Infof(ctx, "Processed ImageToVideo request imageSize=%v model_id=%v took=%v", req.Image.FileSize(), *req.ModelId, took) - res := results[0] - - // Assume only single rendition right now - seg := res.TranscodeData.Segments[0] - name := fmt.Sprintf("%v.mp4", core.RandomManifestID()) - segData := bytes.NewReader(seg.Data) - uri, err := res.OS.SaveData(ctx, name, segData, nil, 0) - if err != nil { - clog.Errorf(ctx, "Could not upload segment err=%q", err) - } - - var result net.TranscodeResult - if err != nil { - clog.Errorf(ctx, "Could not transcode err=%q", err) - result = net.TranscodeResult{Result: &net.TranscodeResult_Error{Error: err.Error()}} - } else { - result = net.TranscodeResult{ - Result: &net.TranscodeResult_Data{ - Data: &net.TranscodeData{ - Segments: []*net.TranscodedSegmentData{ - {Url: uri, Pixels: seg.Pixels}, - }, - Sig: res.Sig, - }, - }, - } - } - - tr := &net.TranscodeResult{ - Result: result.Result, - // TODO: Add other fields - } - - buf, err := proto.Marshal(tr) - if err != nil { - respondWithError(w, err.Error(), http.StatusInternalServerError) - return - } - + w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - w.Write(buf) + _ = json.NewEncoder(w).Encode(resp) }) } diff --git a/server/ai_process.go b/server/ai_process.go index 14fbc46ef..d43ed193b 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -4,20 +4,18 @@ import ( "bufio" "bytes" "context" + "encoding/json" "errors" "io" - "net/http" "path/filepath" "strings" "time" "github.com/cenkalti/backoff" - "github.com/golang/protobuf/proto" "github.com/livepeer/ai-worker/worker" "github.com/livepeer/go-livepeer/clog" "github.com/livepeer/go-livepeer/common" "github.com/livepeer/go-livepeer/core" - "github.com/livepeer/go-livepeer/net" "github.com/livepeer/go-tools/drivers" ) @@ -84,7 +82,7 @@ func processTextToImage(ctx context.Context, params aiRequestParams, req worker. return nil, err } - newMedia[i] = worker.Media{Url: newUrl} + newMedia[i] = worker.Media{Url: newUrl, Seed: media.Seed} } resp.Images = newMedia @@ -155,7 +153,7 @@ func processImageToImage(ctx context.Context, params aiRequestParams, req worker return nil, err } - newMedia[i] = worker.Media{Url: newUrl} + newMedia[i] = worker.Media{Url: newUrl, Seed: media.Seed} } resp.Images = newMedia @@ -204,10 +202,10 @@ func processImageToVideo(ctx context.Context, params aiRequestParams, req worker orchUrl := orchInfos[0].Transcoder - var urls []string + var resp *worker.ImageResponse op := func() error { var err error - urls, err = submitImageToVideo(ctx, orchUrl, req) + resp, err = submitImageToVideo(ctx, orchUrl, req) return err } notify := func(err error, dur time.Duration) { @@ -220,42 +218,43 @@ func processImageToVideo(ctx context.Context, params aiRequestParams, req worker } // HACK: Re-use worker.ImageResponse to return results - videos := make([]worker.Media, len(urls)) - for i, url := range urls { - data, err := downloadSeg(ctx, url) + videos := make([]worker.Media, len(resp.Images)) + for i, media := range resp.Images { + data, err := downloadSeg(ctx, media.Url) if err != nil { return nil, err } - name := filepath.Base(url) + name := filepath.Base(media.Url) newUrl, err := params.os.SaveData(ctx, name, bytes.NewReader(data), nil, 0) if err != nil { return nil, err } videos[i] = worker.Media{ - Url: newUrl, + Url: newUrl, + Seed: media.Seed, } } - resp := &worker.ImageResponse{Images: videos} + resp.Images = videos + return resp, nil } -func submitImageToVideo(ctx context.Context, url string, req worker.ImageToVideoMultipartRequestBody) ([]string, error) { +func submitImageToVideo(ctx context.Context, url string, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { var buf bytes.Buffer mw, err := worker.NewImageToVideoMultipartWriter(&buf, req) if err != nil { return nil, err } - r, err := http.NewRequestWithContext(ctx, "POST", url+"/image-to-video", &buf) + client, err := worker.NewClientWithResponses(url, worker.WithHTTPClient(httpClient)) if err != nil { return nil, err } - r.Header.Set("Content-Type", mw.FormDataContentType()) - resp, err := sendReqWithTimeout(r, imageToVideoTimeout) + resp, err := client.ImageToVideoWithBody(ctx, mw.FormDataContentType(), &buf) if err != nil { return nil, err } @@ -270,25 +269,10 @@ func submitImageToVideo(ctx context.Context, url string, req worker.ImageToVideo return nil, errors.New(string(data)) } - var tr net.TranscodeResult - if err := proto.Unmarshal(data, &tr); err != nil { + var res worker.ImageResponse + if err := json.Unmarshal(data, &res); err != nil { return nil, err } - var tdata *net.TranscodeData - switch res := tr.Result.(type) { - case *net.TranscodeResult_Error: - return nil, errors.New(res.Error) - case *net.TranscodeResult_Data: - tdata = res.Data - default: - return nil, errors.New("UnknownResponse") - } - - urls := make([]string, len(tdata.Segments)) - for i, seg := range tdata.Segments { - urls[i] = seg.Url - } - - return urls, nil + return &res, nil } diff --git a/server/rpc.go b/server/rpc.go index 60841a130..ae59e4cc8 100644 --- a/server/rpc.go +++ b/server/rpc.go @@ -63,7 +63,7 @@ type Orchestrator interface { AuthToken(sessionID string, expiration int64) *net.AuthToken TextToImage(ctx context.Context, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) ImageToImage(ctx context.Context, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) - ImageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) ([]*core.TranscodeResult, error) + ImageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) } // Balance describes methods for a session's balance maintenance From 04c751cb7165a87b7de9f234b0da253b294eb871 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Tue, 13 Feb 2024 03:30:47 +0000 Subject: [PATCH 038/203] multi: Support specifying external containers --- cmd/livepeer/starter/starter.go | 22 ++++++++---------- core/ai.go | 40 ++++++++++++++++++++++++++++++++- go.mod | 2 +- go.sum | 2 ++ 4 files changed, 51 insertions(+), 15 deletions(-) diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index 89bb69139..8430366df 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -523,22 +523,18 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { return } - warmModels := make(map[string]string) if *cfg.AIModels != "" { - models := strings.Split(*cfg.AIModels, ",") - for _, m := range models { - parts := strings.Split(m, ":") - pipeline := parts[0] - modelID := parts[1] - - warmModels[pipeline] = modelID + configs, err := core.ParseAIModelConfigs(*cfg.AIModels) + if err != nil { + glog.Error("Error parsing -aiModels: %v", err) + return } - } - for pipeline, modelID := range warmModels { - if err := n.AIWorker.Warm(ctx, pipeline, modelID); err != nil { - glog.Errorf("Error AI worker warming %v container: %v", pipeline, err) - return + for _, config := range configs { + if err := n.AIWorker.Warm(ctx, config.Pipeline, config.ModelID, config.Endpoint); err != nil { + glog.Errorf("Error AI worker warming %v container: %v", config.Pipeline, err) + return + } } } diff --git a/core/ai.go b/core/ai.go index 264cd7d37..0875af696 100644 --- a/core/ai.go +++ b/core/ai.go @@ -2,6 +2,9 @@ package core import ( "context" + "encoding/json" + "os" + "strings" "github.com/livepeer/ai-worker/worker" ) @@ -10,6 +13,41 @@ type AI interface { TextToImage(context.Context, worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) ImageToImage(context.Context, worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) ImageToVideo(context.Context, worker.ImageToVideoMultipartRequestBody) (*worker.VideoResponse, error) - Warm(context.Context, string, string) error + Warm(context.Context, string, string, string) error Stop(context.Context) error } + +type AIModelConfig struct { + Pipeline string `json:"pipeline"` + ModelID string `json:"model_id"` + Endpoint string `json:"endpoint,omitempty"` +} + +func ParseAIModelConfigs(config string) ([]AIModelConfig, error) { + var configs []AIModelConfig + + info, err := os.Stat(config) + if err == nil && !info.IsDir() { + data, err := os.ReadFile(config) + if err != nil { + return nil, err + } + + if err := json.Unmarshal(data, &configs); err != nil { + return nil, err + } + + return configs, nil + } + + models := strings.Split(config, ",") + for _, m := range models { + parts := strings.Split(m, ":") + pipeline := parts[0] + modelID := parts[1] + + configs = append(configs, AIModelConfig{Pipeline: pipeline, ModelID: modelID}) + } + + return configs, nil +} diff --git a/go.mod b/go.mod index 93aafd3b0..f8c495907 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.0-20240208183357-408784061f26 + github.com/livepeer/ai-worker v0.0.0-20240213025928-df80da87debe github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 diff --git a/go.sum b/go.sum index 2c89897c2..e141d77d0 100644 --- a/go.sum +++ b/go.sum @@ -545,6 +545,8 @@ github.com/livepeer/ai-worker v0.0.0-20240208153040-7c92507e2a40 h1:vVbuu5wqrzq6 github.com/livepeer/ai-worker v0.0.0-20240208153040-7c92507e2a40/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= github.com/livepeer/ai-worker v0.0.0-20240208183357-408784061f26 h1:N15ODU1wgHrjdLtB3vKQrhq2+Y6aatcA6kk21bLvCeM= github.com/livepeer/ai-worker v0.0.0-20240208183357-408784061f26/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= +github.com/livepeer/ai-worker v0.0.0-20240213025928-df80da87debe h1:CNFxlDJxPfsaxp9Vj4Z/4II1mkpP4amtjy8Irkp1AGM= +github.com/livepeer/ai-worker v0.0.0-20240213025928-df80da87debe/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= From 9f47d4e0e8cf7577983f0f464a8f84ef3ce8b58a Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Tue, 13 Feb 2024 17:07:14 +0000 Subject: [PATCH 039/203] multi: Pass bearer token to runners --- cmd/livepeer/starter/starter.go | 3 ++- core/ai.go | 5 +++-- go.mod | 7 ++++--- go.sum | 35 +++++++++------------------------ server/ai_http.go | 1 + server/ai_mediaserver.go | 1 + 6 files changed, 20 insertions(+), 32 deletions(-) diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index 8430366df..f15ba30be 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -531,7 +531,8 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { } for _, config := range configs { - if err := n.AIWorker.Warm(ctx, config.Pipeline, config.ModelID, config.Endpoint); err != nil { + endpoint := worker.RunnerEndpoint{URL: config.URL, Token: config.Token} + if err := n.AIWorker.Warm(ctx, config.Pipeline, config.ModelID, endpoint); err != nil { glog.Errorf("Error AI worker warming %v container: %v", config.Pipeline, err) return } diff --git a/core/ai.go b/core/ai.go index 0875af696..bd0b009ad 100644 --- a/core/ai.go +++ b/core/ai.go @@ -13,14 +13,15 @@ type AI interface { TextToImage(context.Context, worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) ImageToImage(context.Context, worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) ImageToVideo(context.Context, worker.ImageToVideoMultipartRequestBody) (*worker.VideoResponse, error) - Warm(context.Context, string, string, string) error + Warm(context.Context, string, string, worker.RunnerEndpoint) error Stop(context.Context) error } type AIModelConfig struct { Pipeline string `json:"pipeline"` ModelID string `json:"model_id"` - Endpoint string `json:"endpoint,omitempty"` + URL string `json:"url,omitempty"` + Token string `json:"token,omitempty"` } func ParseAIModelConfigs(config string) ([]AIModelConfig, error) { diff --git a/go.mod b/go.mod index f8c495907..6563ae033 100644 --- a/go.mod +++ b/go.mod @@ -6,13 +6,13 @@ require ( contrib.go.opencensus.io/exporter/prometheus v0.4.2 github.com/cenkalti/backoff v2.2.1+incompatible github.com/ethereum/go-ethereum v1.13.5 - github.com/getkin/kin-openapi v0.118.0 + github.com/getkin/kin-openapi v0.122.0 github.com/golang/glog v1.1.1 github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.0-20240213025928-df80da87debe + github.com/livepeer/ai-worker v0.0.0-20240213170524-1c860ab6e412 github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 @@ -59,6 +59,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/deckarep/golang-set/v2 v2.1.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect + github.com/deepmap/oapi-codegen/v2 v2.1.0 // indirect github.com/distribution/reference v0.5.0 // indirect github.com/dlclark/regexp2 v1.7.0 // indirect github.com/docker/cli v24.0.5+incompatible // indirect @@ -152,7 +153,7 @@ require ( github.com/opencontainers/image-spec v1.1.0-rc5 // indirect github.com/opencontainers/runc v1.1.5 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect - github.com/perimeterx/marshmallow v1.1.4 // indirect + github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/go.sum b/go.sum index e141d77d0..95ce7a50f 100644 --- a/go.sum +++ b/go.sum @@ -154,6 +154,8 @@ github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= +github.com/deepmap/oapi-codegen/v2 v2.1.0 h1:I/NMVhJCtuvL9x+S2QzZKpSjGi33oDZwPRdemvOZWyQ= +github.com/deepmap/oapi-codegen/v2 v2.1.0/go.mod h1:R1wL226vc5VmCNJUvMyYr3hJMm5reyv25j952zAVXZ8= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= @@ -202,8 +204,8 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= 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/getkin/kin-openapi v0.118.0 h1:z43njxPmJ7TaPpMSCQb7PN0dEYno4tyBPQcrFdHoLuM= -github.com/getkin/kin-openapi v0.118.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= +github.com/getkin/kin-openapi v0.122.0 h1:WB9Jbl0Hp/T79/JF9xlSW5Kl9uYdk/AWD0yAd9HOM10= +github.com/getkin/kin-openapi v0.122.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= @@ -232,10 +234,8 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= @@ -375,7 +375,6 @@ github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= -github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= @@ -533,20 +532,8 @@ github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4n github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/livepeer/ai-worker v0.0.0-20240126224247-72596a17c8d3 h1:f1TV81ip1OBJnE4daSJtXxPqfYlzMg7e/c0vC2GJe18= -github.com/livepeer/ai-worker v0.0.0-20240126224247-72596a17c8d3/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= -github.com/livepeer/ai-worker v0.0.0-20240201020921-29ff04ceacad h1:gRfTlvHljDQ/eeFNsZF91zkWYs6Se5Br2ARksW+y9O8= -github.com/livepeer/ai-worker v0.0.0-20240201020921-29ff04ceacad/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= -github.com/livepeer/ai-worker v0.0.0-20240202211855-823caeaa265f h1:8owDNiBfN0j62k5YQKm5sk24HQquyKvviUKoB3ok8mM= -github.com/livepeer/ai-worker v0.0.0-20240202211855-823caeaa265f/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= -github.com/livepeer/ai-worker v0.0.0-20240205185039-5c4895915580 h1:7ACCHUpeJsoWADgST/nWfGD0LVRSXFcYG6FTGvzUGn4= -github.com/livepeer/ai-worker v0.0.0-20240205185039-5c4895915580/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= -github.com/livepeer/ai-worker v0.0.0-20240208153040-7c92507e2a40 h1:vVbuu5wqrzq6M6Rlutk0eZv6qZ/kO2OrqQv5n6yt57s= -github.com/livepeer/ai-worker v0.0.0-20240208153040-7c92507e2a40/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= -github.com/livepeer/ai-worker v0.0.0-20240208183357-408784061f26 h1:N15ODU1wgHrjdLtB3vKQrhq2+Y6aatcA6kk21bLvCeM= -github.com/livepeer/ai-worker v0.0.0-20240208183357-408784061f26/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= -github.com/livepeer/ai-worker v0.0.0-20240213025928-df80da87debe h1:CNFxlDJxPfsaxp9Vj4Z/4II1mkpP4amtjy8Irkp1AGM= -github.com/livepeer/ai-worker v0.0.0-20240213025928-df80da87debe/go.mod h1:3+A2/SYTqs+551SKTPy20AVnB8b0Yp26Va5SY37eQ/4= +github.com/livepeer/ai-worker v0.0.0-20240213170524-1c860ab6e412 h1:zc6XbU0KvJ8C2jDdsZP8IkWeKN0TCQMIUyl3fOEydoU= +github.com/livepeer/ai-worker v0.0.0-20240213170524-1c860ab6e412/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= @@ -557,8 +544,6 @@ github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 h1:4A6geMb+HfxBBfaS2 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69/go.mod h1:Hr/JhxxPDipOVd4ZrGYWrdJfpVF8/SEI0nNr2ctAlkM= github.com/livepeer/m3u8 v0.11.1 h1:VkUJzfNTyjy9mqsgp5JPvouwna8wGZMvd/gAfT5FinU= github.com/livepeer/m3u8 v0.11.1/go.mod h1:IUqAtwWPAG2CblfQa4SVzTQoDcEMPyfNOaBSxqHMS04= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -688,8 +673,8 @@ github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/perimeterx/marshmallow v1.1.4 h1:pZLDH9RjlLGGorbXhcaQLhfuV0pFMNfPO55FuFkxqLw= -github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= +github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= +github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/peterbourgon/ff/v3 v3.4.0 h1:QBvM/rizZM1cB0p0lGMdmR7HxZeI/ZrBWB4DqLkMUBc= github.com/peterbourgon/ff/v3 v3.4.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM= @@ -811,11 +796,9 @@ github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+F github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= -github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= diff --git a/server/ai_http.go b/server/ai_http.go index c6343a454..0e098eed0 100644 --- a/server/ai_http.go +++ b/server/ai_http.go @@ -24,6 +24,7 @@ func startAIServer(lp lphttp) error { opts := &middleware.Options{ Options: openapi3filter.Options{ ExcludeRequestBody: true, + AuthenticationFunc: openapi3filter.NoopAuthenticationFunc, }, ErrorHandler: func(w http.ResponseWriter, message string, statusCode int) { clog.Errorf(context.Background(), "oapi validation error statusCode=%v message=%v", statusCode, message) diff --git a/server/ai_mediaserver.go b/server/ai_mediaserver.go index 3f610c63e..74bf2ab89 100644 --- a/server/ai_mediaserver.go +++ b/server/ai_mediaserver.go @@ -53,6 +53,7 @@ func startAIMediaServer(ls *LivepeerServer) error { opts := &middleware.Options{ Options: openapi3filter.Options{ ExcludeRequestBody: true, + AuthenticationFunc: openapi3filter.NoopAuthenticationFunc, }, ErrorHandler: func(w http.ResponseWriter, message string, statusCode int) { clog.Errorf(context.Background(), "oapi validation error statusCode=%v message=%v", statusCode, message) From 0b31700357f9fe094618c85c9d68f1d572fb5373 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Wed, 14 Feb 2024 16:47:45 +0000 Subject: [PATCH 040/203] mod: Bump livepeer/ai-worker --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 6563ae033..a8c861b8f 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.0-20240213170524-1c860ab6e412 + github.com/livepeer/ai-worker v0.0.0-20240214164547-fbdca26a9b2d github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 diff --git a/go.sum b/go.sum index 95ce7a50f..b1457e8d5 100644 --- a/go.sum +++ b/go.sum @@ -534,6 +534,8 @@ github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+O github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= github.com/livepeer/ai-worker v0.0.0-20240213170524-1c860ab6e412 h1:zc6XbU0KvJ8C2jDdsZP8IkWeKN0TCQMIUyl3fOEydoU= github.com/livepeer/ai-worker v0.0.0-20240213170524-1c860ab6e412/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= +github.com/livepeer/ai-worker v0.0.0-20240214164547-fbdca26a9b2d h1:YpBW6wqwpQ9ffgXPnCEoMbsQEbCT5G2AbTb91jrD6BU= +github.com/livepeer/ai-worker v0.0.0-20240214164547-fbdca26a9b2d/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= From c5427d7de267a6ea214976cd0afd9d432e4068b6 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Wed, 14 Feb 2024 22:34:18 +0000 Subject: [PATCH 041/203] mod: Bump livepeer/ai-worker --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index a8c861b8f..3ae5a5329 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.0-20240214164547-fbdca26a9b2d + github.com/livepeer/ai-worker v0.0.0-20240214223314-3d355743417a github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 diff --git a/go.sum b/go.sum index b1457e8d5..6f1a2c36c 100644 --- a/go.sum +++ b/go.sum @@ -536,6 +536,8 @@ github.com/livepeer/ai-worker v0.0.0-20240213170524-1c860ab6e412 h1:zc6XbU0KvJ8C github.com/livepeer/ai-worker v0.0.0-20240213170524-1c860ab6e412/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= github.com/livepeer/ai-worker v0.0.0-20240214164547-fbdca26a9b2d h1:YpBW6wqwpQ9ffgXPnCEoMbsQEbCT5G2AbTb91jrD6BU= github.com/livepeer/ai-worker v0.0.0-20240214164547-fbdca26a9b2d/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= +github.com/livepeer/ai-worker v0.0.0-20240214223314-3d355743417a h1:LZwlUatQLU7rkICGMmca71DE67iGhln/QttAehEs8LA= +github.com/livepeer/ai-worker v0.0.0-20240214223314-3d355743417a/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= From 5e73dcbad4d5d69a87d2714aa7eb7eab208973be Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Fri, 16 Feb 2024 22:41:07 +0000 Subject: [PATCH 042/203] multi: Model cap constraints + multi-O for text-to-image --- cmd/livepeer/starter/starter.go | 48 ++- core/ai.go | 13 +- core/capabilities.go | 92 ++++- net/lp_rpc.pb.go | 701 ++++++++++++++++++-------------- net/lp_rpc.proto | 8 +- server/ai_process.go | 81 +++- 6 files changed, 614 insertions(+), 329 deletions(-) diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index f15ba30be..6cdc448b4 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -496,15 +496,18 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { } } + var aiCaps []core.Capability + constraints := make(map[core.Capability]*core.Constraints) + if *cfg.AIWorker { - if *cfg.Nvidia == "" { - glog.Error("-nvidia required when using -aiworker") - return - } - gpus, err := common.ParseAccelDevices(*cfg.Nvidia, ffmpeg.Nvidia) - if err != nil { - glog.Errorf("Error parsing -nvidia for devices: %v", err) - return + gpus := []string{} + if *cfg.Nvidia != "" { + var err error + gpus, err = common.ParseAccelDevices(*cfg.Nvidia, ffmpeg.Nvidia) + if err != nil { + glog.Errorf("Error parsing -nvidia for devices: %v", err) + return + } } modelsDir := *cfg.AIModelsDir @@ -531,10 +534,29 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { } for _, config := range configs { - endpoint := worker.RunnerEndpoint{URL: config.URL, Token: config.Token} - if err := n.AIWorker.Warm(ctx, config.Pipeline, config.ModelID, endpoint); err != nil { - glog.Errorf("Error AI worker warming %v container: %v", config.Pipeline, err) - return + modelConstraint := &core.ModelConstraint{Warm: config.Warm} + + // If the config contains a URL we call Warm() anyway because AIWorker will just register + // the endpoint for an external container + if config.Warm || config.URL != "" { + endpoint := worker.RunnerEndpoint{URL: config.URL, Token: config.Token} + if err := n.AIWorker.Warm(ctx, config.Pipeline, config.ModelID, endpoint); err != nil { + glog.Errorf("Error AI worker warming %v container: %v", config.Pipeline, err) + return + } + } + + switch config.Pipeline { + case "text-to-image": + _, ok := constraints[core.Capability_TextToImage] + if !ok { + aiCaps = append(aiCaps, core.Capability_TextToImage) + constraints[core.Capability_TextToImage] = &core.Constraints{ + Models: make(map[string]*core.ModelConstraint), + } + } + + constraints[core.Capability_TextToImage].Models[config.ModelID] = modelConstraint } } } @@ -1191,7 +1213,7 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { *cfg.CliAddr = defaultAddr(*cfg.CliAddr, "127.0.0.1", TranscoderCliPort) } - n.Capabilities = core.NewCapabilities(transcoderCaps, core.MandatoryOCapabilities()) + n.Capabilities = core.NewCapabilitiesWithConstraints(append(transcoderCaps, aiCaps...), core.MandatoryOCapabilities(), constraints) if drivers.NodeStorage == nil { // base URI will be empty for broadcasters; that's OK diff --git a/core/ai.go b/core/ai.go index bd0b009ad..0c8ec041c 100644 --- a/core/ai.go +++ b/core/ai.go @@ -3,7 +3,9 @@ package core import ( "context" "encoding/json" + "errors" "os" + "strconv" "strings" "github.com/livepeer/ai-worker/worker" @@ -22,6 +24,7 @@ type AIModelConfig struct { ModelID string `json:"model_id"` URL string `json:"url,omitempty"` Token string `json:"token,omitempty"` + Warm bool `json:"warm,omitempty"` } func ParseAIModelConfigs(config string) ([]AIModelConfig, error) { @@ -44,10 +47,18 @@ func ParseAIModelConfigs(config string) ([]AIModelConfig, error) { models := strings.Split(config, ",") for _, m := range models { parts := strings.Split(m, ":") + if len(parts) < 3 { + return nil, errors.New("invalid AI model config expected ::") + } + pipeline := parts[0] modelID := parts[1] + warm, err := strconv.ParseBool(parts[3]) + if err != nil { + return nil, err + } - configs = append(configs, AIModelConfig{Pipeline: pipeline, ModelID: modelID}) + configs = append(configs, AIModelConfig{Pipeline: pipeline, ModelID: modelID, Warm: warm}) } return configs, nil diff --git a/core/capabilities.go b/core/capabilities.go index 9d54c41e1..d557371f2 100644 --- a/core/capabilities.go +++ b/core/capabilities.go @@ -11,13 +11,23 @@ import ( "github.com/livepeer/lpms/ffmpeg" ) +type ModelConstraints map[string]*ModelConstraint + +type ModelConstraint struct { + Warm bool +} + type Capability int type CapabilityString []uint64 -type Constraints struct{} +type Constraints struct { + // Models contains a *ModelConstraint for each supported model ID + Models ModelConstraints +} +type CapabilityConstraints map[Capability]*Constraints type Capabilities struct { bitstring CapabilityString mandatories CapabilityString - constraints Constraints + constraints CapabilityConstraints capacities map[Capability]int mutex sync.Mutex } @@ -57,6 +67,7 @@ const ( Capability_H264_Decode_422_10bit Capability_H264_Decode_420_10bit Capability_SegmentSlicing + Capability_TextToImage ) var CapabilityNameLookup = map[Capability]string{ @@ -88,6 +99,7 @@ var CapabilityNameLookup = map[Capability]string{ Capability_H264_Decode_422_10bit: "H264 Decode YUV422 10-bit", Capability_H264_Decode_420_10bit: "H264 Decode YUV420 10-bit", Capability_SegmentSlicing: "Segment slicing", + Capability_TextToImage: "Text to image", } var CapabilityTestLookup = map[Capability]CapabilityTest{ @@ -173,6 +185,7 @@ func OptionalCapabilities() []Capability { Capability_H264_Decode_444_10bit, Capability_H264_Decode_422_10bit, Capability_H264_Decode_420_10bit, + Capability_TextToImage, } } @@ -208,6 +221,43 @@ func (c1 CapabilityString) CompatibleWith(c2 CapabilityString) bool { return true } +func (c1 CapabilityConstraints) CompatibleWith(c2 CapabilityConstraints) bool { + for c1Cap, c1Constraints := range c1 { + c2Constraints, ok := c2[c1Cap] + if !ok { + // No constraints on this capability so assume compatibility + continue + } + + if !c1Constraints.CompatibleWith(c2Constraints) { + return false + } + } + + return true +} + +func (c1 *Constraints) CompatibleWith(c2 *Constraints) bool { + return c1.Models.CompatibleWith(c2.Models) +} + +func (c1 ModelConstraints) CompatibleWith(c2 ModelConstraints) bool { + for c1ModelID, c1ModelConstraint := range c1 { + c2ModelConstraint, ok := c2[c1ModelID] + if !ok { + // c2 does not support this model ID so it is incompatible + return false + } + + if c1ModelConstraint.Warm && !c2ModelConstraint.Warm { + // c1 requires the model ID to be warm, but c2's model ID is not warm so it is incompatible + return false + } + } + + return true +} + type chromaDepth struct { Chroma ffmpeg.ChromaSubsampling Depth ffmpeg.ColorDepthBits @@ -337,6 +387,11 @@ func (bcast *Capabilities) CompatibleWith(orch *net.Capabilities) bool { return false } + orchConstraints := CapabilitiesFromNetCapabilities(orch).constraints + if !bcast.constraints.CompatibleWith(orchConstraints) { + return false + } + return bcast.bitstring.CompatibleWith(orch.Bitstring) } @@ -346,10 +401,22 @@ func (c *Capabilities) ToNetCapabilities() *net.Capabilities { } c.mutex.Lock() defer c.mutex.Unlock() - netCaps := &net.Capabilities{Bitstring: c.bitstring, Mandatories: c.mandatories, Capacities: make(map[uint32]uint32)} + netCaps := &net.Capabilities{Bitstring: c.bitstring, Mandatories: c.mandatories, Capacities: make(map[uint32]uint32), Constraints: make(map[uint32]*net.Capabilities_Constraints)} for capability, capacity := range c.capacities { netCaps.Capacities[uint32(capability)] = uint32(capacity) } + for capability, constraints := range c.constraints { + models := make(map[string]*net.Capabilities_Constraints_ModelConstraint) + for modelID, modelConstraint := range constraints.Models { + models[modelID] = &net.Capabilities_Constraints_ModelConstraint{ + Warm: modelConstraint.Warm, + } + } + + netCaps.Constraints[uint32(capability)] = &net.Capabilities_Constraints{ + Models: models, + } + } return netCaps } @@ -361,6 +428,7 @@ func CapabilitiesFromNetCapabilities(caps *net.Capabilities) *Capabilities { bitstring: caps.Bitstring, mandatories: caps.Mandatories, capacities: make(map[Capability]int), + constraints: make(map[Capability]*Constraints), } if caps.Capacities == nil || len(caps.Capacities) == 0 { // build capacities map if not present (struct received from previous versions) @@ -377,6 +445,18 @@ func CapabilitiesFromNetCapabilities(caps *net.Capabilities) *Capabilities { coreCaps.capacities[Capability(capabilityInt)] = int(capacity) } } + + for capabilityInt, constraints := range caps.Constraints { + models := make(map[string]*ModelConstraint) + for modelID, modelConstraint := range constraints.Models { + models[modelID] = &ModelConstraint{Warm: modelConstraint.Warm} + } + + coreCaps.constraints[Capability(capabilityInt)] = &Constraints{ + Models: models, + } + } + return coreCaps } @@ -395,6 +475,12 @@ func NewCapabilities(caps []Capability, m []Capability) *Capabilities { return c } +func NewCapabilitiesWithConstraints(caps []Capability, m []Capability, constraints CapabilityConstraints) *Capabilities { + c := NewCapabilities(caps, m) + c.constraints = constraints + return c +} + func (cap *Capabilities) AddCapacity(newCaps *Capabilities) { cap.mutex.Lock() defer cap.mutex.Unlock() diff --git a/net/lp_rpc.pb.go b/net/lp_rpc.pb.go index 5e6ac7029..58d8af473 100644 --- a/net/lp_rpc.pb.go +++ b/net/lp_rpc.pb.go @@ -686,7 +686,8 @@ type Capabilities struct { // Bit string of features that are required to be supported Mandatories []uint64 `protobuf:"varint,2,rep,packed,name=mandatories,proto3" json:"mandatories,omitempty"` // Capacity corresponding to each capability - Capacities map[uint32]uint32 `protobuf:"bytes,3,rep,name=capacities,proto3" json:"capacities,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` + Capacities map[uint32]uint32 `protobuf:"bytes,3,rep,name=capacities,proto3" json:"capacities,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` + Constraints map[uint32]*Capabilities_Constraints `protobuf:"bytes,4,rep,name=constraints,proto3" json:"constraints,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *Capabilities) Reset() { @@ -742,6 +743,13 @@ func (x *Capabilities) GetCapacities() map[uint32]uint32 { return nil } +func (x *Capabilities) GetConstraints() map[uint32]*Capabilities_Constraints { + if x != nil { + return x.Constraints + } + return nil +} + // The orchestrator sends this in response to `GetOrchestrator`, containing // miscellaneous data related to the job. type OrchestratorInfo struct { @@ -1989,6 +1997,8 @@ type Capabilities_Constraints struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + + Models map[string]*Capabilities_Constraints_ModelConstraint `protobuf:"bytes,1,rep,name=models,proto3" json:"models,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *Capabilities_Constraints) Reset() { @@ -2023,6 +2033,60 @@ func (*Capabilities_Constraints) Descriptor() ([]byte, []int) { return file_net_lp_rpc_proto_rawDescGZIP(), []int{7, 1} } +func (x *Capabilities_Constraints) GetModels() map[string]*Capabilities_Constraints_ModelConstraint { + if x != nil { + return x.Models + } + return nil +} + +type Capabilities_Constraints_ModelConstraint struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Warm bool `protobuf:"varint,1,opt,name=warm,proto3" json:"warm,omitempty"` +} + +func (x *Capabilities_Constraints_ModelConstraint) Reset() { + *x = Capabilities_Constraints_ModelConstraint{} + if protoimpl.UnsafeEnabled { + mi := &file_net_lp_rpc_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Capabilities_Constraints_ModelConstraint) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Capabilities_Constraints_ModelConstraint) ProtoMessage() {} + +func (x *Capabilities_Constraints_ModelConstraint) ProtoReflect() protoreflect.Message { + mi := &file_net_lp_rpc_proto_msgTypes[25] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Capabilities_Constraints_ModelConstraint.ProtoReflect.Descriptor instead. +func (*Capabilities_Constraints_ModelConstraint) Descriptor() ([]byte, []int) { + return file_net_lp_rpc_proto_rawDescGZIP(), []int{7, 1, 0} +} + +func (x *Capabilities_Constraints_ModelConstraint) GetWarm() bool { + if x != nil { + return x.Warm + } + return false +} + var File_net_lp_rpc_proto protoreflect.FileDescriptor var file_net_lp_rpc_proto_rawDesc = []byte{ @@ -2066,7 +2130,7 @@ var file_net_lp_rpc_proto_rawDesc = []byte{ 0x52, 0x0c, 0x70, 0x72, 0x69, 0x63, 0x65, 0x50, 0x65, 0x72, 0x55, 0x6e, 0x69, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x50, 0x65, 0x72, 0x55, 0x6e, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x50, 0x65, 0x72, - 0x55, 0x6e, 0x69, 0x74, 0x22, 0xdf, 0x01, 0x0a, 0x0c, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, + 0x55, 0x6e, 0x69, 0x74, 0x22, 0xd9, 0x04, 0x0a, 0x0c, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x62, 0x69, 0x74, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x03, 0x28, 0x04, 0x52, 0x09, 0x62, 0x69, 0x74, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x20, 0x0a, 0x0b, 0x6d, 0x61, 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x69, @@ -2075,234 +2139,258 @@ var file_net_lp_rpc_proto_rawDesc = []byte{ 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x63, 0x61, - 0x70, 0x61, 0x63, 0x69, 0x74, 0x69, 0x65, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x43, 0x61, 0x70, 0x61, - 0x63, 0x69, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x0d, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x73, 0x74, - 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x22, 0xc0, 0x02, 0x0a, 0x10, 0x4f, 0x72, 0x63, 0x68, 0x65, - 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1e, 0x0a, 0x0a, 0x74, - 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x36, 0x0a, 0x0d, 0x74, - 0x69, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x50, - 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x0c, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x73, 0x12, 0x2d, 0x0a, 0x0a, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, - 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x72, - 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x70, 0x72, 0x69, 0x63, 0x65, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x35, 0x0a, 0x0c, - 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, - 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, - 0x69, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, - 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x41, 0x75, - 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, - 0x65, 0x6e, 0x12, 0x25, 0x0a, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x20, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4f, 0x53, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x22, 0x60, 0x0a, 0x09, 0x41, 0x75, 0x74, - 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, - 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x65, - 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xf4, 0x04, 0x0a, 0x07, - 0x53, 0x65, 0x67, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x6e, 0x69, 0x66, - 0x65, 0x73, 0x74, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6d, 0x61, 0x6e, - 0x69, 0x66, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x65, 0x71, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x73, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, - 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1a, 0x0a, - 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x64, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x64, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x35, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, - 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, + 0x70, 0x61, 0x63, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x44, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, - 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x2d, - 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, - 0x65, 0x6e, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x30, 0x0a, - 0x14, 0x63, 0x61, 0x6c, 0x63, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, - 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x63, 0x61, 0x6c, - 0x63, 0x50, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x48, 0x61, 0x73, 0x68, 0x12, - 0x25, 0x0a, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x20, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x0b, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4f, 0x53, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x73, - 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x35, 0x0a, 0x0c, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x72, - 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x21, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, - 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, - 0x0c, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x37, 0x0a, - 0x0d, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x32, 0x18, 0x22, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, - 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x0d, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x72, 0x6f, - 0x66, 0x69, 0x6c, 0x65, 0x73, 0x32, 0x12, 0x37, 0x0a, 0x0d, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x72, - 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x33, 0x18, 0x23, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, - 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, - 0x52, 0x0d, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x33, 0x12, - 0x41, 0x0a, 0x12, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, - 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x25, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6e, 0x65, - 0x74, 0x2e, 0x53, 0x65, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x52, - 0x11, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, - 0x72, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x26, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, + 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x1a, 0x3d, + 0x0a, 0x0f, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0xe1, 0x01, + 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x41, 0x0a, + 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, + 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, + 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x6f, 0x64, + 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, + 0x1a, 0x25, 0x0a, 0x0f, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, + 0x69, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x77, 0x61, 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x04, 0x77, 0x61, 0x72, 0x6d, 0x1a, 0x68, 0x0a, 0x0b, 0x4d, 0x6f, 0x64, 0x65, 0x6c, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x43, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, + 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x74, + 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x43, 0x6f, 0x6e, 0x73, + 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x1a, 0x5d, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x33, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, + 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, + 0x61, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x22, 0xc0, 0x02, 0x0a, 0x10, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, + 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x72, 0x61, 0x6e, 0x73, + 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x36, 0x0a, 0x0d, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x5f, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, + 0x65, 0x74, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, + 0x0c, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x2d, 0x0a, + 0x0a, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x72, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x09, 0x70, 0x72, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x0a, 0x07, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x35, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, + 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, + 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, + 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x2d, 0x0a, + 0x0a, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x25, 0x0a, 0x07, + 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x20, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0b, 0x2e, + 0x6e, 0x65, 0x74, 0x2e, 0x4f, 0x53, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x73, 0x74, 0x6f, 0x72, + 0x61, 0x67, 0x65, 0x22, 0x60, 0x0a, 0x09, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xf4, 0x04, 0x0a, 0x07, 0x53, 0x65, 0x67, 0x44, 0x61, 0x74, + 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x49, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x49, + 0x64, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x65, 0x71, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, + 0x73, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x03, 0x73, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x35, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, + 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, + 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61, + 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x68, + 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6e, + 0x65, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x09, 0x61, 0x75, + 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x30, 0x0a, 0x14, 0x63, 0x61, 0x6c, 0x63, 0x5f, + 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x63, 0x61, 0x6c, 0x63, 0x50, 0x65, 0x72, 0x63, 0x65, + 0x70, 0x74, 0x75, 0x61, 0x6c, 0x48, 0x61, 0x73, 0x68, 0x12, 0x25, 0x0a, 0x07, 0x73, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x18, 0x20, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6e, 0x65, 0x74, + 0x2e, 0x4f, 0x53, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x12, 0x35, 0x0a, 0x0c, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, + 0x18, 0x21, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, + 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x0c, 0x66, 0x75, 0x6c, 0x6c, 0x50, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x0d, 0x66, 0x75, 0x6c, 0x6c, 0x50, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x32, 0x18, 0x22, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, + 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, + 0x65, 0x52, 0x0d, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x32, + 0x12, 0x37, 0x0a, 0x0d, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, + 0x33, 0x18, 0x23, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, + 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x0d, 0x66, 0x75, 0x6c, 0x6c, + 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x33, 0x12, 0x41, 0x0a, 0x12, 0x73, 0x65, 0x67, + 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, + 0x25, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x65, 0x67, 0x50, + 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x52, 0x11, 0x73, 0x65, 0x67, 0x6d, 0x65, + 0x6e, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x69, 0x6e, - 0x69, 0x74, 0x22, 0x33, 0x0a, 0x0d, 0x53, 0x65, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, - 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x02, 0x74, 0x6f, 0x22, 0xcc, 0x05, 0x0a, 0x0c, 0x56, 0x69, 0x64, 0x65, - 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, - 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x11, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x77, 0x69, 0x64, - 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x12, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x69, - 0x74, 0x72, 0x61, 0x74, 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x62, 0x69, 0x74, - 0x72, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x70, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x03, 0x66, 0x70, 0x73, 0x12, 0x30, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, - 0x18, 0x15, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, - 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, - 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x70, 0x73, 0x44, - 0x65, 0x6e, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x66, 0x70, 0x73, 0x44, 0x65, 0x6e, - 0x12, 0x33, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x17, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x19, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, - 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x07, 0x70, 0x72, - 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x6f, 0x70, 0x18, 0x18, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x03, 0x67, 0x6f, 0x70, 0x12, 0x36, 0x0a, 0x07, 0x65, 0x6e, 0x63, 0x6f, 0x64, - 0x65, 0x72, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, - 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x56, 0x69, 0x64, 0x65, - 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x52, 0x07, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, - 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x44, 0x65, 0x70, 0x74, 0x68, 0x18, 0x1a, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x44, 0x65, 0x70, 0x74, 0x68, 0x12, - 0x47, 0x0a, 0x0c, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, - 0x1b, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, 0x65, - 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x53, - 0x75, 0x62, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x52, 0x0c, 0x63, 0x68, 0x72, 0x6f, - 0x6d, 0x61, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x71, 0x75, 0x61, 0x6c, - 0x69, 0x74, 0x79, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x71, 0x75, 0x61, 0x6c, 0x69, - 0x74, 0x79, 0x22, 0x1d, 0x0a, 0x06, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x0a, 0x0a, 0x06, - 0x4d, 0x50, 0x45, 0x47, 0x54, 0x53, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4d, 0x50, 0x34, 0x10, - 0x01, 0x22, 0x6a, 0x0a, 0x07, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x13, 0x0a, 0x0f, - 0x45, 0x4e, 0x43, 0x4f, 0x44, 0x45, 0x52, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, - 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x48, 0x32, 0x36, 0x34, 0x5f, 0x42, 0x41, 0x53, 0x45, 0x4c, 0x49, - 0x4e, 0x45, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x48, 0x32, 0x36, 0x34, 0x5f, 0x4d, 0x41, 0x49, - 0x4e, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x48, 0x32, 0x36, 0x34, 0x5f, 0x48, 0x49, 0x47, 0x48, - 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x48, 0x32, 0x36, 0x34, 0x5f, 0x43, 0x4f, 0x4e, 0x53, 0x54, - 0x52, 0x41, 0x49, 0x4e, 0x45, 0x44, 0x5f, 0x48, 0x49, 0x47, 0x48, 0x10, 0x04, 0x22, 0x32, 0x0a, - 0x0a, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x12, 0x08, 0x0a, 0x04, 0x48, - 0x32, 0x36, 0x34, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x32, 0x36, 0x35, 0x10, 0x01, 0x12, - 0x07, 0x0a, 0x03, 0x56, 0x50, 0x38, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x56, 0x50, 0x39, 0x10, - 0x03, 0x22, 0x43, 0x0a, 0x11, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x53, 0x75, 0x62, 0x73, 0x61, - 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x48, 0x52, 0x4f, 0x4d, 0x41, - 0x5f, 0x34, 0x32, 0x30, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x48, 0x52, 0x4f, 0x4d, 0x41, - 0x5f, 0x34, 0x32, 0x32, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x48, 0x52, 0x4f, 0x4d, 0x41, - 0x5f, 0x34, 0x34, 0x34, 0x10, 0x02, 0x22, 0x71, 0x0a, 0x15, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, - 0x6f, 0x64, 0x65, 0x64, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x12, - 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, - 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x06, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x70, 0x65, 0x72, - 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x75, 0x72, 0x6c, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, - 0x61, 0x6c, 0x48, 0x61, 0x73, 0x68, 0x55, 0x72, 0x6c, 0x22, 0x59, 0x0a, 0x0d, 0x54, 0x72, 0x61, - 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x36, 0x0a, 0x08, 0x73, 0x65, - 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6e, - 0x65, 0x74, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x53, 0x65, 0x67, - 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, - 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x03, 0x73, 0x69, 0x67, 0x22, 0x9a, 0x01, 0x0a, 0x0f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, - 0x64, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x65, 0x71, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x73, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x05, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x12, 0x28, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x12, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, - 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x29, 0x0a, 0x04, - 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6e, 0x65, 0x74, - 0x2e, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x42, 0x08, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x22, 0x7c, 0x0a, 0x0f, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, - 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, - 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x35, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61, - 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, - 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, - 0x73, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x22, - 0x89, 0x01, 0x0a, 0x0d, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, - 0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x75, 0x72, 0x6c, 0x12, 0x26, 0x0a, 0x07, 0x73, 0x65, 0x67, 0x44, 0x61, 0x74, 0x61, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x65, 0x67, 0x44, 0x61, - 0x74, 0x61, 0x52, 0x07, 0x73, 0x65, 0x67, 0x44, 0x61, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x74, - 0x61, 0x73, 0x6b, 0x49, 0x64, 0x18, 0x10, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x74, 0x61, 0x73, - 0x6b, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, - 0x11, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x4a, - 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x21, 0x10, 0x22, 0x22, 0x9f, 0x02, 0x0a, 0x0c, - 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x1c, 0x0a, 0x09, - 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x61, - 0x63, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, - 0x66, 0x61, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x77, 0x69, 0x6e, - 0x5f, 0x70, 0x72, 0x6f, 0x62, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x77, 0x69, 0x6e, - 0x50, 0x72, 0x6f, 0x62, 0x12, 0x2e, 0x0a, 0x13, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, - 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x64, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x11, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x6e, 0x64, - 0x48, 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x04, 0x73, 0x65, 0x65, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x78, 0x70, 0x69, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x0f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x12, 0x48, 0x0a, 0x11, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, - 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x70, 0x69, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x10, 0x65, 0x78, 0x70, - 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x49, 0x0a, - 0x12, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x6e, 0x6f, - 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x73, 0x65, 0x6e, 0x64, 0x65, - 0x72, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x22, 0x7a, 0x0a, 0x16, 0x54, 0x69, 0x63, 0x6b, + 0x69, 0x74, 0x18, 0x26, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x69, 0x6e, 0x69, 0x74, 0x22, 0x33, 0x0a, 0x0d, + 0x53, 0x65, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, + 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x66, 0x72, 0x6f, + 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x74, + 0x6f, 0x22, 0xcc, 0x05, 0x0a, 0x0c, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, + 0x11, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x12, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x72, 0x61, 0x74, 0x65, 0x18, + 0x13, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x62, 0x69, 0x74, 0x72, 0x61, 0x74, 0x65, 0x12, 0x10, + 0x0a, 0x03, 0x66, 0x70, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x66, 0x70, 0x73, + 0x12, 0x30, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x18, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x2e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x70, 0x73, 0x44, 0x65, 0x6e, 0x18, 0x16, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x06, 0x66, 0x70, 0x73, 0x44, 0x65, 0x6e, 0x12, 0x33, 0x0a, 0x07, 0x70, 0x72, + 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x6e, 0x65, + 0x74, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x50, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, + 0x10, 0x0a, 0x03, 0x67, 0x6f, 0x70, 0x18, 0x18, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x67, 0x6f, + 0x70, 0x12, 0x36, 0x0a, 0x07, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x18, 0x19, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, + 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, + 0x52, 0x07, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, + 0x6f, 0x72, 0x44, 0x65, 0x70, 0x74, 0x68, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x63, + 0x6f, 0x6c, 0x6f, 0x72, 0x44, 0x65, 0x70, 0x74, 0x68, 0x12, 0x47, 0x0a, 0x0c, 0x63, 0x68, 0x72, + 0x6f, 0x6d, 0x61, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x23, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x53, 0x75, 0x62, 0x73, 0x61, 0x6d, 0x70, + 0x6c, 0x69, 0x6e, 0x67, 0x52, 0x0c, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x46, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x1c, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x07, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x22, 0x1d, 0x0a, 0x06, + 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x50, 0x45, 0x47, 0x54, 0x53, + 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4d, 0x50, 0x34, 0x10, 0x01, 0x22, 0x6a, 0x0a, 0x07, 0x50, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x13, 0x0a, 0x0f, 0x45, 0x4e, 0x43, 0x4f, 0x44, 0x45, + 0x52, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x48, + 0x32, 0x36, 0x34, 0x5f, 0x42, 0x41, 0x53, 0x45, 0x4c, 0x49, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x0d, + 0x0a, 0x09, 0x48, 0x32, 0x36, 0x34, 0x5f, 0x4d, 0x41, 0x49, 0x4e, 0x10, 0x02, 0x12, 0x0d, 0x0a, + 0x09, 0x48, 0x32, 0x36, 0x34, 0x5f, 0x48, 0x49, 0x47, 0x48, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, + 0x48, 0x32, 0x36, 0x34, 0x5f, 0x43, 0x4f, 0x4e, 0x53, 0x54, 0x52, 0x41, 0x49, 0x4e, 0x45, 0x44, + 0x5f, 0x48, 0x49, 0x47, 0x48, 0x10, 0x04, 0x22, 0x32, 0x0a, 0x0a, 0x56, 0x69, 0x64, 0x65, 0x6f, + 0x43, 0x6f, 0x64, 0x65, 0x63, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x32, 0x36, 0x34, 0x10, 0x00, 0x12, + 0x08, 0x0a, 0x04, 0x48, 0x32, 0x36, 0x35, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x56, 0x50, 0x38, + 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x56, 0x50, 0x39, 0x10, 0x03, 0x22, 0x43, 0x0a, 0x11, 0x43, + 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x53, 0x75, 0x62, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, + 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x48, 0x52, 0x4f, 0x4d, 0x41, 0x5f, 0x34, 0x32, 0x30, 0x10, 0x00, + 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x48, 0x52, 0x4f, 0x4d, 0x41, 0x5f, 0x34, 0x32, 0x32, 0x10, 0x01, + 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x48, 0x52, 0x4f, 0x4d, 0x41, 0x5f, 0x34, 0x34, 0x34, 0x10, 0x02, + 0x22, 0x71, 0x0a, 0x15, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x53, 0x65, + 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x70, + 0x69, 0x78, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x70, 0x69, 0x78, + 0x65, 0x6c, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, + 0x6c, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x11, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x48, 0x61, 0x73, 0x68, + 0x55, 0x72, 0x6c, 0x22, 0x59, 0x0a, 0x0d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, + 0x44, 0x61, 0x74, 0x61, 0x12, 0x36, 0x0a, 0x08, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x61, + 0x74, 0x61, 0x52, 0x08, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, + 0x73, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x22, 0x9a, + 0x01, 0x0a, 0x0f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x65, 0x71, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x03, 0x73, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x28, 0x0a, 0x04, + 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6e, 0x65, 0x74, + 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, + 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x29, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x10, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4f, 0x72, 0x63, 0x68, 0x65, + 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e, 0x66, + 0x6f, 0x42, 0x08, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x7c, 0x0a, 0x0f, 0x52, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, + 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, + 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, + 0x74, 0x79, 0x12, 0x35, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, + 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, + 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x0c, 0x63, 0x61, 0x70, + 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x22, 0x89, 0x01, 0x0a, 0x0d, 0x4e, 0x6f, + 0x74, 0x69, 0x66, 0x79, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, + 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x26, 0x0a, + 0x07, 0x73, 0x65, 0x67, 0x44, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, + 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x65, 0x67, 0x44, 0x61, 0x74, 0x61, 0x52, 0x07, 0x73, 0x65, + 0x67, 0x44, 0x61, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x18, + 0x10, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x12, 0x1a, 0x0a, + 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, + 0x04, 0x08, 0x21, 0x10, 0x22, 0x22, 0x9f, 0x02, 0x0a, 0x0c, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, + 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, + 0x69, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x66, 0x61, 0x63, 0x65, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x77, 0x69, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x62, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x77, 0x69, 0x6e, 0x50, 0x72, 0x6f, 0x62, 0x12, 0x2e, + 0x0a, 0x13, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x64, + 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x72, 0x65, 0x63, + 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x6e, 0x64, 0x48, 0x61, 0x73, 0x68, 0x12, 0x12, + 0x0a, 0x04, 0x73, 0x65, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x73, 0x65, + 0x65, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x65, 0x78, + 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x48, 0x0a, + 0x11, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, + 0x69, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, + 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x10, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x49, 0x0a, 0x12, 0x54, 0x69, 0x63, 0x6b, 0x65, + 0x74, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x21, 0x0a, + 0x0c, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x4e, 0x6f, 0x6e, 0x63, 0x65, + 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, + 0x69, 0x67, 0x22, 0x7a, 0x0a, 0x16, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x70, 0x69, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x25, 0x0a, 0x0e, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x6f, + 0x75, 0x6e, 0x64, 0x12, 0x39, 0x0a, 0x19, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x16, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x22, 0xa5, + 0x02, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x36, 0x0a, 0x0d, 0x74, 0x69, + 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x52, 0x0c, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x48, 0x0a, 0x11, 0x65, 0x78, + 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, - 0x6d, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, - 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x63, 0x72, 0x65, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x39, 0x0a, 0x19, 0x63, 0x72, 0x65, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x16, 0x63, 0x72, - 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x48, 0x61, 0x73, 0x68, 0x22, 0xa5, 0x02, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x12, 0x36, 0x0a, 0x0d, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x69, - 0x63, 0x6b, 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x0c, 0x74, 0x69, 0x63, 0x6b, - 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, - 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, - 0x12, 0x48, 0x0a, 0x11, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, - 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6e, 0x65, - 0x74, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x10, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x49, 0x0a, 0x14, 0x74, 0x69, - 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x72, 0x61, - 0x6d, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, - 0x69, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, - 0x73, 0x52, 0x12, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x50, - 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x35, 0x0a, 0x0e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, - 0x64, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, - 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x72, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, 0x65, - 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x72, 0x69, 0x63, 0x65, 0x32, 0xd8, 0x01, 0x0a, - 0x0c, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x42, 0x0a, - 0x0f, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, - 0x12, 0x18, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, - 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x6e, 0x65, 0x74, - 0x2e, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, - 0x6f, 0x12, 0x5e, 0x0a, 0x15, 0x45, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, - 0x69, 0x6e, 0x67, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x2e, 0x6e, 0x65, 0x74, - 0x2e, 0x45, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x53, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, - 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, - 0x6e, 0x67, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x24, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x0d, 0x2e, 0x6e, 0x65, 0x74, 0x2e, - 0x50, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6e, 0x67, 0x1a, 0x0d, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, - 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6e, 0x67, 0x32, 0x4e, 0x0a, 0x0a, 0x54, 0x72, 0x61, 0x6e, 0x73, - 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x40, 0x0a, 0x12, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, - 0x72, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x14, 0x2e, 0x6e, 0x65, - 0x74, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x12, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, 0x65, - 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x42, 0x07, 0x5a, 0x05, 0x2e, 0x2f, 0x6e, 0x65, 0x74, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6d, 0x73, 0x52, 0x10, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x12, 0x49, 0x0a, 0x14, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x73, + 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x53, + 0x65, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x12, 0x74, 0x69, 0x63, + 0x6b, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, + 0x35, 0x0a, 0x0e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x63, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x72, + 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, + 0x64, 0x50, 0x72, 0x69, 0x63, 0x65, 0x32, 0xd8, 0x01, 0x0a, 0x0c, 0x4f, 0x72, 0x63, 0x68, 0x65, + 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x42, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x4f, 0x72, + 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x18, 0x2e, 0x6e, 0x65, 0x74, + 0x2e, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4f, 0x72, 0x63, 0x68, 0x65, + 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x5e, 0x0a, 0x15, 0x45, + 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, + 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x50, + 0x69, 0x6e, 0x67, 0x12, 0x0d, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x50, 0x6f, + 0x6e, 0x67, 0x1a, 0x0d, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6e, + 0x67, 0x32, 0x4e, 0x0a, 0x0a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, + 0x40, 0x0a, 0x12, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x14, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x52, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6e, 0x65, + 0x74, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x30, + 0x01, 0x42, 0x07, 0x5a, 0x05, 0x2e, 0x2f, 0x6e, 0x65, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -2318,82 +2406,89 @@ func file_net_lp_rpc_proto_rawDescGZIP() []byte { } var file_net_lp_rpc_proto_enumTypes = make([]protoimpl.EnumInfo, 5) -var file_net_lp_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 24) +var file_net_lp_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 27) var file_net_lp_rpc_proto_goTypes = []interface{}{ - (OSInfo_StorageType)(0), // 0: net.OSInfo.StorageType - (VideoProfile_Format)(0), // 1: net.VideoProfile.Format - (VideoProfile_Profile)(0), // 2: net.VideoProfile.Profile - (VideoProfile_VideoCodec)(0), // 3: net.VideoProfile.VideoCodec - (VideoProfile_ChromaSubsampling)(0), // 4: net.VideoProfile.ChromaSubsampling - (*PingPong)(nil), // 5: net.PingPong - (*EndTranscodingSessionRequest)(nil), // 6: net.EndTranscodingSessionRequest - (*EndTranscodingSessionResponse)(nil), // 7: net.EndTranscodingSessionResponse - (*OrchestratorRequest)(nil), // 8: net.OrchestratorRequest - (*OSInfo)(nil), // 9: net.OSInfo - (*S3OSInfo)(nil), // 10: net.S3OSInfo - (*PriceInfo)(nil), // 11: net.PriceInfo - (*Capabilities)(nil), // 12: net.Capabilities - (*OrchestratorInfo)(nil), // 13: net.OrchestratorInfo - (*AuthToken)(nil), // 14: net.AuthToken - (*SegData)(nil), // 15: net.SegData - (*SegParameters)(nil), // 16: net.SegParameters - (*VideoProfile)(nil), // 17: net.VideoProfile - (*TranscodedSegmentData)(nil), // 18: net.TranscodedSegmentData - (*TranscodeData)(nil), // 19: net.TranscodeData - (*TranscodeResult)(nil), // 20: net.TranscodeResult - (*RegisterRequest)(nil), // 21: net.RegisterRequest - (*NotifySegment)(nil), // 22: net.NotifySegment - (*TicketParams)(nil), // 23: net.TicketParams - (*TicketSenderParams)(nil), // 24: net.TicketSenderParams - (*TicketExpirationParams)(nil), // 25: net.TicketExpirationParams - (*Payment)(nil), // 26: net.Payment - nil, // 27: net.Capabilities.CapacitiesEntry - (*Capabilities_Constraints)(nil), // 28: net.Capabilities.Constraints + (OSInfo_StorageType)(0), // 0: net.OSInfo.StorageType + (VideoProfile_Format)(0), // 1: net.VideoProfile.Format + (VideoProfile_Profile)(0), // 2: net.VideoProfile.Profile + (VideoProfile_VideoCodec)(0), // 3: net.VideoProfile.VideoCodec + (VideoProfile_ChromaSubsampling)(0), // 4: net.VideoProfile.ChromaSubsampling + (*PingPong)(nil), // 5: net.PingPong + (*EndTranscodingSessionRequest)(nil), // 6: net.EndTranscodingSessionRequest + (*EndTranscodingSessionResponse)(nil), // 7: net.EndTranscodingSessionResponse + (*OrchestratorRequest)(nil), // 8: net.OrchestratorRequest + (*OSInfo)(nil), // 9: net.OSInfo + (*S3OSInfo)(nil), // 10: net.S3OSInfo + (*PriceInfo)(nil), // 11: net.PriceInfo + (*Capabilities)(nil), // 12: net.Capabilities + (*OrchestratorInfo)(nil), // 13: net.OrchestratorInfo + (*AuthToken)(nil), // 14: net.AuthToken + (*SegData)(nil), // 15: net.SegData + (*SegParameters)(nil), // 16: net.SegParameters + (*VideoProfile)(nil), // 17: net.VideoProfile + (*TranscodedSegmentData)(nil), // 18: net.TranscodedSegmentData + (*TranscodeData)(nil), // 19: net.TranscodeData + (*TranscodeResult)(nil), // 20: net.TranscodeResult + (*RegisterRequest)(nil), // 21: net.RegisterRequest + (*NotifySegment)(nil), // 22: net.NotifySegment + (*TicketParams)(nil), // 23: net.TicketParams + (*TicketSenderParams)(nil), // 24: net.TicketSenderParams + (*TicketExpirationParams)(nil), // 25: net.TicketExpirationParams + (*Payment)(nil), // 26: net.Payment + nil, // 27: net.Capabilities.CapacitiesEntry + (*Capabilities_Constraints)(nil), // 28: net.Capabilities.Constraints + nil, // 29: net.Capabilities.ConstraintsEntry + (*Capabilities_Constraints_ModelConstraint)(nil), // 30: net.Capabilities.Constraints.ModelConstraint + nil, // 31: net.Capabilities.Constraints.ModelsEntry } var file_net_lp_rpc_proto_depIdxs = []int32{ 14, // 0: net.EndTranscodingSessionRequest.auth_token:type_name -> net.AuthToken 0, // 1: net.OSInfo.storageType:type_name -> net.OSInfo.StorageType 10, // 2: net.OSInfo.s3info:type_name -> net.S3OSInfo 27, // 3: net.Capabilities.capacities:type_name -> net.Capabilities.CapacitiesEntry - 23, // 4: net.OrchestratorInfo.ticket_params:type_name -> net.TicketParams - 11, // 5: net.OrchestratorInfo.price_info:type_name -> net.PriceInfo - 12, // 6: net.OrchestratorInfo.capabilities:type_name -> net.Capabilities - 14, // 7: net.OrchestratorInfo.auth_token:type_name -> net.AuthToken - 9, // 8: net.OrchestratorInfo.storage:type_name -> net.OSInfo - 12, // 9: net.SegData.capabilities:type_name -> net.Capabilities - 14, // 10: net.SegData.auth_token:type_name -> net.AuthToken - 9, // 11: net.SegData.storage:type_name -> net.OSInfo - 17, // 12: net.SegData.fullProfiles:type_name -> net.VideoProfile - 17, // 13: net.SegData.fullProfiles2:type_name -> net.VideoProfile - 17, // 14: net.SegData.fullProfiles3:type_name -> net.VideoProfile - 16, // 15: net.SegData.segment_parameters:type_name -> net.SegParameters - 1, // 16: net.VideoProfile.format:type_name -> net.VideoProfile.Format - 2, // 17: net.VideoProfile.profile:type_name -> net.VideoProfile.Profile - 3, // 18: net.VideoProfile.encoder:type_name -> net.VideoProfile.VideoCodec - 4, // 19: net.VideoProfile.chromaFormat:type_name -> net.VideoProfile.ChromaSubsampling - 18, // 20: net.TranscodeData.segments:type_name -> net.TranscodedSegmentData - 19, // 21: net.TranscodeResult.data:type_name -> net.TranscodeData - 13, // 22: net.TranscodeResult.info:type_name -> net.OrchestratorInfo - 12, // 23: net.RegisterRequest.capabilities:type_name -> net.Capabilities - 15, // 24: net.NotifySegment.segData:type_name -> net.SegData - 25, // 25: net.TicketParams.expiration_params:type_name -> net.TicketExpirationParams - 23, // 26: net.Payment.ticket_params:type_name -> net.TicketParams - 25, // 27: net.Payment.expiration_params:type_name -> net.TicketExpirationParams - 24, // 28: net.Payment.ticket_sender_params:type_name -> net.TicketSenderParams - 11, // 29: net.Payment.expected_price:type_name -> net.PriceInfo - 8, // 30: net.Orchestrator.GetOrchestrator:input_type -> net.OrchestratorRequest - 6, // 31: net.Orchestrator.EndTranscodingSession:input_type -> net.EndTranscodingSessionRequest - 5, // 32: net.Orchestrator.Ping:input_type -> net.PingPong - 21, // 33: net.Transcoder.RegisterTranscoder:input_type -> net.RegisterRequest - 13, // 34: net.Orchestrator.GetOrchestrator:output_type -> net.OrchestratorInfo - 7, // 35: net.Orchestrator.EndTranscodingSession:output_type -> net.EndTranscodingSessionResponse - 5, // 36: net.Orchestrator.Ping:output_type -> net.PingPong - 22, // 37: net.Transcoder.RegisterTranscoder:output_type -> net.NotifySegment - 34, // [34:38] is the sub-list for method output_type - 30, // [30:34] is the sub-list for method input_type - 30, // [30:30] is the sub-list for extension type_name - 30, // [30:30] is the sub-list for extension extendee - 0, // [0:30] is the sub-list for field type_name + 29, // 4: net.Capabilities.constraints:type_name -> net.Capabilities.ConstraintsEntry + 23, // 5: net.OrchestratorInfo.ticket_params:type_name -> net.TicketParams + 11, // 6: net.OrchestratorInfo.price_info:type_name -> net.PriceInfo + 12, // 7: net.OrchestratorInfo.capabilities:type_name -> net.Capabilities + 14, // 8: net.OrchestratorInfo.auth_token:type_name -> net.AuthToken + 9, // 9: net.OrchestratorInfo.storage:type_name -> net.OSInfo + 12, // 10: net.SegData.capabilities:type_name -> net.Capabilities + 14, // 11: net.SegData.auth_token:type_name -> net.AuthToken + 9, // 12: net.SegData.storage:type_name -> net.OSInfo + 17, // 13: net.SegData.fullProfiles:type_name -> net.VideoProfile + 17, // 14: net.SegData.fullProfiles2:type_name -> net.VideoProfile + 17, // 15: net.SegData.fullProfiles3:type_name -> net.VideoProfile + 16, // 16: net.SegData.segment_parameters:type_name -> net.SegParameters + 1, // 17: net.VideoProfile.format:type_name -> net.VideoProfile.Format + 2, // 18: net.VideoProfile.profile:type_name -> net.VideoProfile.Profile + 3, // 19: net.VideoProfile.encoder:type_name -> net.VideoProfile.VideoCodec + 4, // 20: net.VideoProfile.chromaFormat:type_name -> net.VideoProfile.ChromaSubsampling + 18, // 21: net.TranscodeData.segments:type_name -> net.TranscodedSegmentData + 19, // 22: net.TranscodeResult.data:type_name -> net.TranscodeData + 13, // 23: net.TranscodeResult.info:type_name -> net.OrchestratorInfo + 12, // 24: net.RegisterRequest.capabilities:type_name -> net.Capabilities + 15, // 25: net.NotifySegment.segData:type_name -> net.SegData + 25, // 26: net.TicketParams.expiration_params:type_name -> net.TicketExpirationParams + 23, // 27: net.Payment.ticket_params:type_name -> net.TicketParams + 25, // 28: net.Payment.expiration_params:type_name -> net.TicketExpirationParams + 24, // 29: net.Payment.ticket_sender_params:type_name -> net.TicketSenderParams + 11, // 30: net.Payment.expected_price:type_name -> net.PriceInfo + 31, // 31: net.Capabilities.Constraints.models:type_name -> net.Capabilities.Constraints.ModelsEntry + 28, // 32: net.Capabilities.ConstraintsEntry.value:type_name -> net.Capabilities.Constraints + 30, // 33: net.Capabilities.Constraints.ModelsEntry.value:type_name -> net.Capabilities.Constraints.ModelConstraint + 8, // 34: net.Orchestrator.GetOrchestrator:input_type -> net.OrchestratorRequest + 6, // 35: net.Orchestrator.EndTranscodingSession:input_type -> net.EndTranscodingSessionRequest + 5, // 36: net.Orchestrator.Ping:input_type -> net.PingPong + 21, // 37: net.Transcoder.RegisterTranscoder:input_type -> net.RegisterRequest + 13, // 38: net.Orchestrator.GetOrchestrator:output_type -> net.OrchestratorInfo + 7, // 39: net.Orchestrator.EndTranscodingSession:output_type -> net.EndTranscodingSessionResponse + 5, // 40: net.Orchestrator.Ping:output_type -> net.PingPong + 22, // 41: net.Transcoder.RegisterTranscoder:output_type -> net.NotifySegment + 38, // [38:42] is the sub-list for method output_type + 34, // [34:38] is the sub-list for method input_type + 34, // [34:34] is the sub-list for extension type_name + 34, // [34:34] is the sub-list for extension extendee + 0, // [0:34] is the sub-list for field type_name } func init() { file_net_lp_rpc_proto_init() } @@ -2678,6 +2773,18 @@ func file_net_lp_rpc_proto_init() { return nil } } + file_net_lp_rpc_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Capabilities_Constraints_ModelConstraint); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_net_lp_rpc_proto_msgTypes[15].OneofWrappers = []interface{}{ (*TranscodeResult_Error)(nil), @@ -2689,7 +2796,7 @@ func file_net_lp_rpc_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_net_lp_rpc_proto_rawDesc, NumEnums: 5, - NumMessages: 24, + NumMessages: 27, NumExtensions: 0, NumServices: 2, }, diff --git a/net/lp_rpc.proto b/net/lp_rpc.proto index c7854f598..35d6e1920 100644 --- a/net/lp_rpc.proto +++ b/net/lp_rpc.proto @@ -107,8 +107,14 @@ message Capabilities { // Non-binary capability constraints, such as supported ranges. message Constraints { - // Empty for now + message ModelConstraint { + bool warm = 1; + } + + map models = 1; } + + map constraints = 4; } // The orchestrator sends this in response to `GetOrchestrator`, containing diff --git a/server/ai_process.go b/server/ai_process.go index d43ed193b..455d62b47 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -8,6 +8,7 @@ import ( "errors" "io" "path/filepath" + "sort" "strings" "time" @@ -16,6 +17,7 @@ import ( "github.com/livepeer/go-livepeer/clog" "github.com/livepeer/go-livepeer/common" "github.com/livepeer/go-livepeer/core" + "github.com/livepeer/go-livepeer/net" "github.com/livepeer/go-tools/drivers" ) @@ -24,6 +26,7 @@ const textToImageRetryBackoff = 10 * time.Second const imageToImageRetryBackoff = 10 * time.Second const imageToVideoRetryBackoff = 1 * time.Minute const maxProcessingRetries = 4 +const aiRequestNumOrchs = 5 type ServiceUnavailableError struct { err error @@ -38,35 +41,85 @@ type aiRequestParams struct { os drivers.OSSession } +func getOrchestratorsForAIRequest(ctx context.Context, params aiRequestParams, cap core.Capability, modelID string, numOrchs int) ([]*net.OrchestratorInfo, error) { + // No warm constraints applied here because we don't want to filter out orchs based on warm criteria at discovery time + // Instead, we want all orchs that support the model and then will prioritize orchs that have a warm model at selection time + constraints := map[core.Capability]*core.Constraints{ + cap: { + Models: map[string]*core.ModelConstraint{ + modelID: { + Warm: false, + }, + }, + }, + } + caps := core.NewCapabilitiesWithConstraints(append(core.DefaultCapabilities(), cap), nil, constraints) + + orchDesc, err := params.node.OrchestratorPool.GetOrchestrators(ctx, numOrchs, newSuspender(), caps, common.ScoreAtLeast(0)) + if err != nil { + return nil, err + } + + return orchDesc.GetRemoteInfos(), nil +} + func processTextToImage(ctx context.Context, params aiRequestParams, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) { - // Discover 1 orchestrator - // TODO: Discover multiple orchestrators - caps := core.NewCapabilities(core.DefaultCapabilities(), nil) - orchDesc, err := params.node.OrchestratorPool.GetOrchestrators(ctx, 1, newSuspender(), caps, common.ScoreAtLeast(0)) + modelID := "" + if req.ModelId != nil { + modelID = *req.ModelId + } + + orchInfos, err := getOrchestratorsForAIRequest(ctx, params, core.Capability_TextToImage, modelID, aiRequestNumOrchs) if err != nil { return nil, err } - orchInfos := orchDesc.GetRemoteInfos() if len(orchInfos) == 0 { return nil, errors.New("no orchestrators available") } - orchUrl := orchInfos[0].Transcoder + // Sort and prioritize orchs that have warm model + sort.Slice(orchInfos, func(i, j int) bool { + iConstraints, ok := orchInfos[i].Capabilities.Constraints[uint32(core.Capability_TextToImage)] + if !ok { + return false + } + + iModelConstraint, ok := iConstraints.Models[modelID] + if !ok { + return false + } + + // If warm i goes before j, else j goes before i + return iModelConstraint.Warm + }) var resp *worker.ImageResponse - op := func() error { + + // Round robin up to maxProcessingRetries times + orchIdx := 0 + tries := 0 + for tries < maxProcessingRetries { + orchUrl := orchInfos[orchIdx].Transcoder + var err error resp, err = submitTextToImage(ctx, orchUrl, req) - return err - } - notify := func(err error, dur time.Duration) { - clog.Infof(ctx, "Error submitting TextToImage request err=%v retrying after dur=%v", err, dur) + if err == nil { + break + } + + clog.Infof(ctx, "Error submitting TextToImage request try=%v orch=%v err=%v", tries, orchUrl, err) + + tries++ + orchIdx++ + // Wrap back around + if orchIdx >= len(orchInfos) { + orchIdx = 0 + } } - b := backoff.WithMaxRetries(backoff.NewConstantBackOff(textToImageRetryBackoff), maxProcessingRetries) - if err := backoff.RetryNotify(op, b, notify); err != nil { - return nil, &ServiceUnavailableError{err: err} + if resp == nil { + return nil, &ServiceUnavailableError{err: errors.New("no orchestrators available")} } newMedia := make([]worker.Media, len(resp.Images)) From c22f398956aac96560b40d782d4c66c14c73972b Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Mon, 19 Feb 2024 20:55:07 +0000 Subject: [PATCH 043/203] multi: Multi-O for image-to-image --- cmd/livepeer/starter/starter.go | 10 ++++ core/capabilities.go | 3 ++ server/ai_process.go | 89 +++++++++++++++++++-------------- 3 files changed, 65 insertions(+), 37 deletions(-) diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index 6cdc448b4..66cf5882e 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -557,6 +557,16 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { } constraints[core.Capability_TextToImage].Models[config.ModelID] = modelConstraint + case "image-to-image": + _, ok := constraints[core.Capability_ImageToImage] + if !ok { + aiCaps = append(aiCaps, core.Capability_ImageToImage) + constraints[core.Capability_ImageToImage] = &core.Constraints{ + Models: make(map[string]*core.ModelConstraint), + } + } + + constraints[core.Capability_ImageToImage].Models[config.ModelID] = modelConstraint } } } diff --git a/core/capabilities.go b/core/capabilities.go index d557371f2..36122f4a6 100644 --- a/core/capabilities.go +++ b/core/capabilities.go @@ -68,6 +68,7 @@ const ( Capability_H264_Decode_420_10bit Capability_SegmentSlicing Capability_TextToImage + Capability_ImageToImage ) var CapabilityNameLookup = map[Capability]string{ @@ -100,6 +101,7 @@ var CapabilityNameLookup = map[Capability]string{ Capability_H264_Decode_420_10bit: "H264 Decode YUV420 10-bit", Capability_SegmentSlicing: "Segment slicing", Capability_TextToImage: "Text to image", + Capability_ImageToImage: "Image to image", } var CapabilityTestLookup = map[Capability]CapabilityTest{ @@ -186,6 +188,7 @@ func OptionalCapabilities() []Capability { Capability_H264_Decode_422_10bit, Capability_H264_Decode_420_10bit, Capability_TextToImage, + Capability_ImageToImage, } } diff --git a/server/ai_process.go b/server/ai_process.go index 455d62b47..4b3e1cb3a 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -22,11 +22,10 @@ import ( ) const imageToVideoTimeout = 5 * time.Minute -const textToImageRetryBackoff = 10 * time.Second -const imageToImageRetryBackoff = 10 * time.Second const imageToVideoRetryBackoff = 1 * time.Minute const maxProcessingRetries = 4 -const aiRequestNumOrchs = 5 +const defaultTextToImageModelID = "stabilityai/sdxl-turbo" +const defaultImageToImageModelID = "stabilityai/sdxl-turbo" type ServiceUnavailableError struct { err error @@ -41,7 +40,7 @@ type aiRequestParams struct { os drivers.OSSession } -func getOrchestratorsForAIRequest(ctx context.Context, params aiRequestParams, cap core.Capability, modelID string, numOrchs int) ([]*net.OrchestratorInfo, error) { +func getOrchestratorsForAIRequest(ctx context.Context, params aiRequestParams, cap core.Capability, modelID string) ([]*net.OrchestratorInfo, error) { // No warm constraints applied here because we don't want to filter out orchs based on warm criteria at discovery time // Instead, we want all orchs that support the model and then will prioritize orchs that have a warm model at selection time constraints := map[core.Capability]*core.Constraints{ @@ -55,32 +54,18 @@ func getOrchestratorsForAIRequest(ctx context.Context, params aiRequestParams, c } caps := core.NewCapabilitiesWithConstraints(append(core.DefaultCapabilities(), cap), nil, constraints) + // Set numOrchs to the pool size so that discovery tries to find maximum # of compatible orchs within a timeout + numOrchs := params.node.OrchestratorPool.Size() orchDesc, err := params.node.OrchestratorPool.GetOrchestrators(ctx, numOrchs, newSuspender(), caps, common.ScoreAtLeast(0)) if err != nil { return nil, err } - return orchDesc.GetRemoteInfos(), nil -} - -func processTextToImage(ctx context.Context, params aiRequestParams, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) { - modelID := "" - if req.ModelId != nil { - modelID = *req.ModelId - } - - orchInfos, err := getOrchestratorsForAIRequest(ctx, params, core.Capability_TextToImage, modelID, aiRequestNumOrchs) - if err != nil { - return nil, err - } - - if len(orchInfos) == 0 { - return nil, errors.New("no orchestrators available") - } + orchInfos := orchDesc.GetRemoteInfos() // Sort and prioritize orchs that have warm model sort.Slice(orchInfos, func(i, j int) bool { - iConstraints, ok := orchInfos[i].Capabilities.Constraints[uint32(core.Capability_TextToImage)] + iConstraints, ok := orchInfos[i].Capabilities.Constraints[uint32(cap)] if !ok { return false } @@ -94,6 +79,24 @@ func processTextToImage(ctx context.Context, params aiRequestParams, req worker. return iModelConstraint.Warm }) + return orchInfos, nil +} + +func processTextToImage(ctx context.Context, params aiRequestParams, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) { + modelID := defaultTextToImageModelID + if req.ModelId != nil { + modelID = *req.ModelId + } + + orchInfos, err := getOrchestratorsForAIRequest(ctx, params, core.Capability_TextToImage, modelID) + if err != nil { + return nil, err + } + + if len(orchInfos) == 0 { + return nil, errors.New("no orchestrators available") + } + var resp *worker.ImageResponse // Round robin up to maxProcessingRetries times @@ -163,34 +166,46 @@ func submitTextToImage(ctx context.Context, url string, req worker.TextToImageJS } func processImageToImage(ctx context.Context, params aiRequestParams, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { - // Discover 1 orchestrator - // TODO: Discover multiple orchestrators - caps := core.NewCapabilities(core.DefaultCapabilities(), nil) - orchDesc, err := params.node.OrchestratorPool.GetOrchestrators(ctx, 1, newSuspender(), caps, common.ScoreAtLeast(0)) + modelID := defaultImageToImageModelID + if req.ModelId != nil { + modelID = *req.ModelId + } + + orchInfos, err := getOrchestratorsForAIRequest(ctx, params, core.Capability_ImageToImage, modelID) if err != nil { return nil, err } - orchInfos := orchDesc.GetRemoteInfos() if len(orchInfos) == 0 { return nil, errors.New("no orchestrators available") } - orchUrl := orchInfos[0].Transcoder - var resp *worker.ImageResponse - op := func() error { + + // Round robin up to maxProcessingRetries times + orchIdx := 0 + tries := 0 + for tries < maxProcessingRetries { + orchUrl := orchInfos[orchIdx].Transcoder + var err error resp, err = submitImageToImage(ctx, orchUrl, req) - return err - } - notify := func(err error, dur time.Duration) { - clog.Infof(ctx, "Error submitting ImageToImage request err=%v retrying after dur=%v", err, dur) + if err == nil { + break + } + + clog.Infof(ctx, "Error submitting ImageToImage request try=%v orch=%v err=%v", tries, orchUrl, err) + + tries++ + orchIdx++ + // Wrap back around + if orchIdx >= len(orchInfos) { + orchIdx = 0 + } } - b := backoff.WithMaxRetries(backoff.NewConstantBackOff(imageToImageRetryBackoff), maxProcessingRetries) - if err := backoff.RetryNotify(op, b, notify); err != nil { - return nil, &ServiceUnavailableError{err: err} + if resp == nil { + return nil, &ServiceUnavailableError{err: errors.New("no orchestrators available")} } newMedia := make([]worker.Media, len(resp.Images)) From 579a31dff2a2d75fe8c76d1374f6fdd4c344c1d1 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Mon, 19 Feb 2024 22:22:49 +0000 Subject: [PATCH 044/203] multi: Multi-O for image-to-video --- cmd/livepeer/starter/starter.go | 10 ++++++++ core/capabilities.go | 3 +++ server/ai_process.go | 45 ++++++++++++++++++++------------- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index 66cf5882e..30386b10f 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -567,6 +567,16 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { } constraints[core.Capability_ImageToImage].Models[config.ModelID] = modelConstraint + case "image-to-video": + _, ok := constraints[core.Capability_ImageToVideo] + if !ok { + aiCaps = append(aiCaps, core.Capability_ImageToVideo) + constraints[core.Capability_ImageToVideo] = &core.Constraints{ + Models: make(map[string]*core.ModelConstraint), + } + } + + constraints[core.Capability_ImageToVideo].Models[config.ModelID] = modelConstraint } } } diff --git a/core/capabilities.go b/core/capabilities.go index 36122f4a6..600a7ff2c 100644 --- a/core/capabilities.go +++ b/core/capabilities.go @@ -69,6 +69,7 @@ const ( Capability_SegmentSlicing Capability_TextToImage Capability_ImageToImage + Capability_ImageToVideo ) var CapabilityNameLookup = map[Capability]string{ @@ -102,6 +103,7 @@ var CapabilityNameLookup = map[Capability]string{ Capability_SegmentSlicing: "Segment slicing", Capability_TextToImage: "Text to image", Capability_ImageToImage: "Image to image", + Capability_ImageToVideo: "Image to video", } var CapabilityTestLookup = map[Capability]CapabilityTest{ @@ -189,6 +191,7 @@ func OptionalCapabilities() []Capability { Capability_H264_Decode_420_10bit, Capability_TextToImage, Capability_ImageToImage, + Capability_ImageToVideo, } } diff --git a/server/ai_process.go b/server/ai_process.go index 4b3e1cb3a..649c116a0 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -10,9 +10,7 @@ import ( "path/filepath" "sort" "strings" - "time" - "github.com/cenkalti/backoff" "github.com/livepeer/ai-worker/worker" "github.com/livepeer/go-livepeer/clog" "github.com/livepeer/go-livepeer/common" @@ -21,11 +19,10 @@ import ( "github.com/livepeer/go-tools/drivers" ) -const imageToVideoTimeout = 5 * time.Minute -const imageToVideoRetryBackoff = 1 * time.Minute const maxProcessingRetries = 4 const defaultTextToImageModelID = "stabilityai/sdxl-turbo" const defaultImageToImageModelID = "stabilityai/sdxl-turbo" +const defaultImageToVideoModelID = "stabilityai/stable-video-diffusion-img2vid-xt" type ServiceUnavailableError struct { err error @@ -255,33 +252,45 @@ func submitImageToImage(ctx context.Context, url string, req worker.ImageToImage } func processImageToVideo(ctx context.Context, params aiRequestParams, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { - // Discover 1 orchestrator - // TODO: Discover multiple orchestrators - caps := core.NewCapabilities(core.DefaultCapabilities(), nil) - orchDesc, err := params.node.OrchestratorPool.GetOrchestrators(ctx, 1, newSuspender(), caps, common.ScoreAtLeast(0)) + modelID := defaultImageToVideoModelID + if req.ModelId != nil { + modelID = *req.ModelId + } + + orchInfos, err := getOrchestratorsForAIRequest(ctx, params, core.Capability_ImageToVideo, modelID) if err != nil { return nil, err } - orchInfos := orchDesc.GetRemoteInfos() if len(orchInfos) == 0 { return nil, errors.New("no orchestrators available") } - orchUrl := orchInfos[0].Transcoder - var resp *worker.ImageResponse - op := func() error { + + // Round robin up to maxProcessingRetries times + orchIdx := 0 + tries := 0 + for tries < maxProcessingRetries { + orchUrl := orchInfos[orchIdx].Transcoder + var err error resp, err = submitImageToVideo(ctx, orchUrl, req) - return err - } - notify := func(err error, dur time.Duration) { - clog.Infof(ctx, "Error submitting ImageToVideo request err=%v retrying after dur=%v", err, dur) + if err == nil { + break + } + + clog.Infof(ctx, "Error submitting ImageToVideo request try=%v orch=%v err=%v", tries, orchUrl, err) + + tries++ + orchIdx++ + // Wrap back around + if orchIdx >= len(orchInfos) { + orchIdx = 0 + } } - b := backoff.WithMaxRetries(backoff.NewConstantBackOff(imageToVideoRetryBackoff), maxProcessingRetries) - if err := backoff.RetryNotify(op, b, notify); err != nil { + if resp == nil { return nil, &ServiceUnavailableError{err: err} } From f82c0145d7c090d809c55d9ef1152405378061d2 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Tue, 20 Feb 2024 21:33:57 +0000 Subject: [PATCH 045/203] mod: Bump livepeer/ai-worker --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 3ae5a5329..3ef60906c 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.0-20240214223314-3d355743417a + github.com/livepeer/ai-worker v0.0.0-20240220213200-59b5b237cd8b github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 diff --git a/go.sum b/go.sum index 6f1a2c36c..d4b9a93d3 100644 --- a/go.sum +++ b/go.sum @@ -538,6 +538,8 @@ github.com/livepeer/ai-worker v0.0.0-20240214164547-fbdca26a9b2d h1:YpBW6wqwpQ9f github.com/livepeer/ai-worker v0.0.0-20240214164547-fbdca26a9b2d/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= github.com/livepeer/ai-worker v0.0.0-20240214223314-3d355743417a h1:LZwlUatQLU7rkICGMmca71DE67iGhln/QttAehEs8LA= github.com/livepeer/ai-worker v0.0.0-20240214223314-3d355743417a/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= +github.com/livepeer/ai-worker v0.0.0-20240220213200-59b5b237cd8b h1:zCQv/A3Pafdr/NutU7zLChkF+XcAdzY0ObjN1E4nr44= +github.com/livepeer/ai-worker v0.0.0-20240220213200-59b5b237cd8b/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= From a61818b45758ae4713d8162e6470f9ff02903e57 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Tue, 20 Feb 2024 23:15:14 +0000 Subject: [PATCH 046/203] core: Fix resolution for image-to-video --- core/orchestrator.go | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/core/orchestrator.go b/core/orchestrator.go index f6f316625..71fac2a45 100644 --- a/core/orchestrator.go +++ b/core/orchestrator.go @@ -540,7 +540,7 @@ func (n *LivepeerNode) sendToTranscodeLoop(ctx context.Context, md *SegTranscodi return res, res.Err } -func (n *LivepeerNode) transcodeFrames(ctx context.Context, sessionID string, urls []string) *TranscodeResult { +func (n *LivepeerNode) transcodeFrames(ctx context.Context, sessionID string, urls []string, inProfile ffmpeg.VideoProfile, outProfile ffmpeg.VideoProfile) *TranscodeResult { ctx = clog.AddOrchSessionID(ctx, sessionID) var fnamep *string @@ -576,19 +576,10 @@ func (n *LivepeerNode) transcodeFrames(ctx context.Context, sessionID string, ur transcoder := n.Transcoder md := &SegTranscodingMetadata{ - Fname: path.Join(dirPath, "%d.png"), - ProfileIn: ffmpeg.VideoProfile{ - Framerate: 7, - FramerateDen: 1, - }, + Fname: path.Join(dirPath, "%d.png"), + ProfileIn: inProfile, Profiles: []ffmpeg.VideoProfile{ - { - Name: "image-to-video", - Resolution: "1024x576", - AspectRatio: "16:9", - Bitrate: "6000k", - Format: ffmpeg.FormatMP4, - }, + outProfile, }, AuthToken: &net.AuthToken{SessionId: sessionID}, } @@ -911,6 +902,28 @@ func (n *LivepeerNode) imageToVideo(ctx context.Context, req worker.ImageToVideo clog.V(common.DEBUG).Infof(ctx, "Generating frames took=%v", took) sessionID := string(RandomManifestID()) + framerate := 7 + if req.Fps != nil { + framerate = *req.Fps + } + inProfile := ffmpeg.VideoProfile{ + Framerate: uint(framerate), + FramerateDen: 1, + } + height := 576 + if req.Height != nil { + height = *req.Height + } + width := 1024 + if req.Width != nil { + width = *req.Width + } + outProfile := ffmpeg.VideoProfile{ + Name: "image-to-video", + Resolution: fmt.Sprintf("%vx%v", width, height), + Bitrate: "6000k", + Format: ffmpeg.FormatMP4, + } // HACK: Re-use worker.ImageResponse to return results // Transcode frames into segments. videos := make([]worker.Media, len(resp.Frames)) @@ -922,7 +935,7 @@ func (n *LivepeerNode) imageToVideo(ctx context.Context, req worker.ImageToVideo } // Transcode slice of frame urls into a segment - res := n.transcodeFrames(ctx, sessionID, urls) + res := n.transcodeFrames(ctx, sessionID, urls, inProfile, outProfile) if res.Err != nil { return nil, res.Err } From 3d77eed91023d7da3cb3594a217062f333e9a3db Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Tue, 20 Feb 2024 23:15:31 +0000 Subject: [PATCH 047/203] server: Fix service unavailable error image-to-video --- server/ai_process.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/ai_process.go b/server/ai_process.go index 649c116a0..ccd339bb7 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -291,7 +291,7 @@ func processImageToVideo(ctx context.Context, params aiRequestParams, req worker } if resp == nil { - return nil, &ServiceUnavailableError{err: err} + return nil, &ServiceUnavailableError{err: errors.New("no orchestrators available")} } // HACK: Re-use worker.ImageResponse to return results From f2bc443d8790d25acccb6163250bf3783471f76a Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Sun, 25 Feb 2024 22:31:38 +0000 Subject: [PATCH 048/203] core: Use software transcoder for image-to-video Temp fix for sporadic CUDA operation not permitted errors with nvidia --- core/orchestrator.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/core/orchestrator.go b/core/orchestrator.go index 71fac2a45..ddecab843 100644 --- a/core/orchestrator.go +++ b/core/orchestrator.go @@ -569,11 +569,12 @@ func (n *LivepeerNode) transcodeFrames(ctx context.Context, sessionID string, ur } } - // Check if there's a transcoder available - if n.Transcoder == nil { - return terr(ErrTranscoderAvail) - } - transcoder := n.Transcoder + // Use local software transcoder instead of node's configured transcoder + // because if the node is using a nvidia transcoder there may be sporadic + // CUDA operation not permitted errors that are difficult to debug. + // The majority of the execution time for image-to-video is the frame generation + // so slower software transcoding should not be a big deal for now. + transcoder := NewLocalTranscoder(n.WorkDir) md := &SegTranscodingMetadata{ Fname: path.Join(dirPath, "%d.png"), From 8cc7430f1d8bd43794f63b5dff2313d781c1d230 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Tue, 27 Feb 2024 20:57:41 +0000 Subject: [PATCH 049/203] server: Return 503 if no Os have cap --- server/ai_process.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/ai_process.go b/server/ai_process.go index ccd339bb7..644e715dd 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -91,7 +91,7 @@ func processTextToImage(ctx context.Context, params aiRequestParams, req worker. } if len(orchInfos) == 0 { - return nil, errors.New("no orchestrators available") + return nil, &ServiceUnavailableError{err: errors.New("no orchestrators available")} } var resp *worker.ImageResponse @@ -174,7 +174,7 @@ func processImageToImage(ctx context.Context, params aiRequestParams, req worker } if len(orchInfos) == 0 { - return nil, errors.New("no orchestrators available") + return nil, &ServiceUnavailableError{err: errors.New("no orchestrators available")} } var resp *worker.ImageResponse @@ -263,7 +263,7 @@ func processImageToVideo(ctx context.Context, params aiRequestParams, req worker } if len(orchInfos) == 0 { - return nil, errors.New("no orchestrators available") + return nil, &ServiceUnavailableError{err: errors.New("no orchestrators available")} } var resp *worker.ImageResponse From f583d932172457745bd2a368da8b25d3cbb49ee9 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Mon, 11 Mar 2024 21:03:02 +0000 Subject: [PATCH 050/203] server: Add AISessionManager For managing the sessions per AI capability + model ID in a way that is compatible with existing broadcast session code --- server/ai_mediaserver.go | 5 +- server/ai_process.go | 42 +++--- server/ai_session.go | 314 +++++++++++++++++++++++++++++++++++++++ server/mediaserver.go | 5 + 4 files changed, 340 insertions(+), 26 deletions(-) create mode 100644 server/ai_session.go diff --git a/server/ai_mediaserver.go b/server/ai_mediaserver.go index 74bf2ab89..ad0e8756a 100644 --- a/server/ai_mediaserver.go +++ b/server/ai_mediaserver.go @@ -87,8 +87,9 @@ func (ls *LivepeerServer) TextToImage() http.Handler { clog.V(common.VERBOSE).Infof(r.Context(), "Received TextToImage request prompt=%v model_id=%v", req.Prompt, *req.ModelId) params := aiRequestParams{ - node: ls.LivepeerNode, - os: drivers.NodeStorage.NewSession(requestID), + node: ls.LivepeerNode, + os: drivers.NodeStorage.NewSession(requestID), + sessManager: ls.AISessionManager, } start := time.Now() diff --git a/server/ai_process.go b/server/ai_process.go index 644e715dd..19a7b7cdb 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -33,8 +33,9 @@ func (e *ServiceUnavailableError) Error() string { } type aiRequestParams struct { - node *core.LivepeerNode - os drivers.OSSession + node *core.LivepeerNode + os drivers.OSSession + sessManager *AISessionManager } func getOrchestratorsForAIRequest(ctx context.Context, params aiRequestParams, cap core.Capability, modelID string) ([]*net.OrchestratorInfo, error) { @@ -85,37 +86,30 @@ func processTextToImage(ctx context.Context, params aiRequestParams, req worker. modelID = *req.ModelId } - orchInfos, err := getOrchestratorsForAIRequest(ctx, params, core.Capability_TextToImage, modelID) - if err != nil { - return nil, err - } - - if len(orchInfos) == 0 { - return nil, &ServiceUnavailableError{err: errors.New("no orchestrators available")} - } - var resp *worker.ImageResponse - // Round robin up to maxProcessingRetries times - orchIdx := 0 tries := 0 for tries < maxProcessingRetries { - orchUrl := orchInfos[orchIdx].Transcoder + sess, err := params.sessManager.Select(ctx, core.Capability_TextToImage, modelID) + if err != nil { + return nil, err + } - var err error - resp, err = submitTextToImage(ctx, orchUrl, req) + if sess == nil { + break + } + + resp, err = submitTextToImage(ctx, params, sess, req) if err == nil { + params.sessManager.Complete(ctx, sess) break } - clog.Infof(ctx, "Error submitting TextToImage request try=%v orch=%v err=%v", tries, orchUrl, err) + clog.Infof(ctx, "Error submitting TextToImage request try=%v orch=%v err=%v", tries, sess.Transcoder(), err) + + params.sessManager.Remove(ctx, sess) tries++ - orchIdx++ - // Wrap back around - if orchIdx >= len(orchInfos) { - orchIdx = 0 - } } if resp == nil { @@ -143,8 +137,8 @@ func processTextToImage(ctx context.Context, params aiRequestParams, req worker. return resp, nil } -func submitTextToImage(ctx context.Context, url string, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) { - client, err := worker.NewClientWithResponses(url, worker.WithHTTPClient(httpClient)) +func submitTextToImage(ctx context.Context, params aiRequestParams, sess *AISession, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) { + client, err := worker.NewClientWithResponses(sess.Transcoder(), worker.WithHTTPClient(httpClient)) if err != nil { return nil, err } diff --git a/server/ai_session.go b/server/ai_session.go new file mode 100644 index 000000000..fc4c347fd --- /dev/null +++ b/server/ai_session.go @@ -0,0 +1,314 @@ +package server + +import ( + "context" + "strconv" + "sync" + "time" + + "github.com/livepeer/go-livepeer/clog" + "github.com/livepeer/go-livepeer/common" + "github.com/livepeer/go-livepeer/core" + "github.com/livepeer/go-tools/drivers" +) + +type AISession struct { + *BroadcastSession + + // Fields used by AISessionSelector for session lifecycle management + Cap core.Capability + ModelID string + Warm bool +} + +type AISessionPool struct { + selector BroadcastSessionsSelector + sessMap map[string]*BroadcastSession + suspender *suspender +} + +func NewAISessionPool(selector BroadcastSessionsSelector, suspender *suspender) *AISessionPool { + return &AISessionPool{ + selector: selector, + sessMap: make(map[string]*BroadcastSession), + suspender: suspender, + } +} + +func (pool *AISessionPool) Select(ctx context.Context) *BroadcastSession { + for { + sess := pool.selector.Select(ctx) + if sess == nil { + return nil + } + + if _, ok := pool.sessMap[sess.Transcoder()]; !ok { + // If the session is not tracked by sessMap skip it + continue + } + + return sess + } +} + +func (pool *AISessionPool) Complete(sess *BroadcastSession) { + existingSess, ok := pool.sessMap[sess.Transcoder()] + if !ok { + // If the session is not tracked by sessMap, skip returning it to the selector + return + } + + if sess != existingSess { + // If the session is tracked by sessMap AND it is different from what is tracked by sessMap + // skip returning it to the selector + return + } + + pool.selector.Complete(sess) +} + +func (pool *AISessionPool) Add(sessions []*BroadcastSession) { + // If we try to add new sessions to the pool the suspender + // should treat this as a refresh + pool.suspender.signalRefresh() + + var uniqueSessions []*BroadcastSession + for _, sess := range sessions { + if _, ok := pool.sessMap[sess.Transcoder()]; ok { + // Skip the session if it is already tracked by sessMap + continue + } + + pool.sessMap[sess.Transcoder()] = sess + uniqueSessions = append(uniqueSessions, sess) + } + + pool.selector.Add(uniqueSessions) +} + +func (pool *AISessionPool) Remove(sess *BroadcastSession) { + delete(pool.sessMap, sess.Transcoder()) + + // Magic number for now + penalty := 3 + // If this method is called assume that the orch should be suspended + // as well + pool.suspender.suspend(sess.Transcoder(), penalty) +} + +type AISessionSelector struct { + // Pool of sessions with orchs that have the requested model warm + warmPool *AISessionPool + // Pool of sessions with orchs that have the requested model cold + coldPool *AISessionPool + // The time until the pools should be refreshed with orchs from discovery + ttl time.Duration + lastRefreshTime time.Time + + cap core.Capability + modelID string + + node *core.LivepeerNode + suspender *suspender + os drivers.OSSession +} + +func NewAISessionSelector(cap core.Capability, modelID string, node *core.LivepeerNode, ttl time.Duration) (*AISessionSelector, error) { + var stakeRdr stakeReader + if node.Eth != nil { + stakeRdr = &storeStakeReader{store: node.Database} + } + + suspender := newSuspender() + + // The latency score in this context is just the latency of the last completed request for a session + // The "good enough" latency score is set to 0.0 so the selector will always select unknown sessions first + minLS := 0.0 + warmPool := NewAISessionPool(NewMinLSSelector(stakeRdr, minLS, node.SelectionAlgorithm, node.OrchPerfScore), suspender) + coldPool := NewAISessionPool(NewMinLSSelector(stakeRdr, minLS, node.SelectionAlgorithm, node.OrchPerfScore), suspender) + sel := &AISessionSelector{ + warmPool: warmPool, + coldPool: coldPool, + ttl: ttl, + cap: cap, + modelID: modelID, + node: node, + suspender: suspender, + os: drivers.NodeStorage.NewSession(strconv.Itoa(int(cap)) + "_" + modelID), + } + + if err := sel.Refresh(context.Background()); err != nil { + return nil, err + } + + return sel, nil +} + +func (sel *AISessionSelector) Select(ctx context.Context) *AISession { + if time.Now().After(sel.lastRefreshTime.Add(sel.ttl)) { + if err := sel.Refresh(ctx); err != nil { + clog.Infof(ctx, "Error refreshing AISessionSelector err=%v", err) + } + } + + sess := sel.warmPool.Select(ctx) + if sess != nil { + return &AISession{BroadcastSession: sess, Cap: sel.cap, ModelID: sel.modelID, Warm: true} + } + + sess = sel.coldPool.Select(ctx) + if sess != nil { + return &AISession{BroadcastSession: sess, Cap: sel.cap, ModelID: sel.modelID, Warm: false} + } + + return nil +} + +func (sel *AISessionSelector) Complete(sess *AISession) { + if sess.Warm { + sel.warmPool.Complete(sess.BroadcastSession) + } else { + sel.coldPool.Complete(sess.BroadcastSession) + } +} + +func (sel *AISessionSelector) Remove(sess *AISession) { + if sess.Warm { + sel.warmPool.Remove(sess.BroadcastSession) + } else { + sel.coldPool.Remove(sess.BroadcastSession) + } +} + +func (sel *AISessionSelector) Refresh(ctx context.Context) error { + sessions, err := sel.getSessions(ctx) + if err != nil { + return err + } + + var warmSessions []*BroadcastSession + var coldSessions []*BroadcastSession + for _, sess := range sessions { + // If the constraints are missing for this capability skip this session + constraints, ok := sess.OrchestratorInfo.Capabilities.Constraints[uint32(sel.cap)] + if !ok { + continue + } + + // If the constraint for the modelID are missing skip this session + modelConstraint, ok := constraints.Models[sel.modelID] + if !ok { + continue + } + + if modelConstraint.Warm { + warmSessions = append(warmSessions, sess) + } else { + coldSessions = append(coldSessions, sess) + } + } + + sel.warmPool.Add(warmSessions) + sel.coldPool.Add(coldSessions) + + sel.lastRefreshTime = time.Now() + + return nil +} + +func (sel *AISessionSelector) getSessions(ctx context.Context) ([]*BroadcastSession, error) { + // No warm constraints applied here because we don't want to filter out orchs based on warm criteria at discovery time + // Instead, we want all orchs that support the model and then will filter for orchs that have a warm model separately + constraints := map[core.Capability]*core.Constraints{ + sel.cap: { + Models: map[string]*core.ModelConstraint{ + sel.modelID: { + Warm: false, + }, + }, + }, + } + caps := core.NewCapabilitiesWithConstraints(append(core.DefaultCapabilities(), sel.cap), nil, constraints) + + // Set numOrchs to the pool size so that discovery tries to find maximum # of compatible orchs within a timeout + numOrchs := sel.node.OrchestratorPool.Size() + + // Use a dummy manifestID specific to the capability + modelID + // Typically, a manifestID would identify a stream + // In the AI context, a manifestID can identify a capability + modelID and each + // request for the capability + modelID can be thought of as a part of the same "stream" + manifestID := strconv.Itoa(int(sel.cap)) + "_" + sel.modelID + streamParams := &core.StreamParameters{ + ManifestID: core.ManifestID(manifestID), + Capabilities: caps, + OS: sel.os, + } + return selectOrchestrator(ctx, sel.node, streamParams, numOrchs, sel.suspender, common.ScoreAtLeast(0)) +} + +type AISessionManager struct { + node *core.LivepeerNode + selectors map[string]*AISessionSelector + mu *sync.Mutex + ttl time.Duration +} + +func NewAISessionManager(node *core.LivepeerNode, ttl time.Duration) *AISessionManager { + return &AISessionManager{ + node: node, + selectors: make(map[string]*AISessionSelector), + mu: &sync.Mutex{}, + ttl: ttl, + } +} + +func (c *AISessionManager) Select(ctx context.Context, cap core.Capability, modelID string) (*AISession, error) { + sel, err := c.getSelector(ctx, cap, modelID) + if err != nil { + return nil, err + } + + return sel.Select(ctx), nil +} + +func (c *AISessionManager) Remove(ctx context.Context, sess *AISession) error { + sel, err := c.getSelector(ctx, sess.Cap, sess.ModelID) + if err != nil { + return err + } + + sel.Remove(sess) + + return nil +} + +func (c *AISessionManager) Complete(ctx context.Context, sess *AISession) error { + sel, err := c.getSelector(ctx, sess.Cap, sess.ModelID) + if err != nil { + return err + } + + sel.Complete(sess) + + return nil +} + +func (c *AISessionManager) getSelector(ctx context.Context, cap core.Capability, modelID string) (*AISessionSelector, error) { + c.mu.Lock() + defer c.mu.Unlock() + + cacheKey := strconv.Itoa(int(cap)) + "_" + modelID + sel, ok := c.selectors[cacheKey] + if !ok { + // Create the selector + var err error + sel, err = NewAISessionSelector(cap, modelID, c.node, c.ttl) + if err != nil { + return nil, err + } + + c.selectors[cacheKey] = sel + } + + return sel, nil +} diff --git a/server/mediaserver.go b/server/mediaserver.go index 522d7fc14..72a445ffb 100644 --- a/server/mediaserver.go +++ b/server/mediaserver.go @@ -59,6 +59,8 @@ const StreamKeyBytes = 6 const SegLen = 2 * time.Second const BroadcastRetry = 15 * time.Second +const AISessionManagerTTL = 10 * time.Minute + var BroadcastJobVideoProfiles = []ffmpeg.VideoProfile{ffmpeg.P240p30fps4x3, ffmpeg.P360p30fps16x9} var AuthWebhookURL *url.URL @@ -108,6 +110,8 @@ type LivepeerServer struct { ExposeCurrentManifest bool recordingsAuthResponses *cache.Cache + AISessionManager *AISessionManager + // Thread sensitive fields. All accesses to the // following fields should be protected by `connectionLock` rtmpConnections map[core.ManifestID]*rtmpConnection @@ -181,6 +185,7 @@ func NewLivepeerServer(rtmpAddr string, lpNode *core.LivepeerNode, httpIngest bo rtmpConnections: make(map[core.ManifestID]*rtmpConnection), internalManifests: make(map[core.ManifestID]core.ManifestID), recordingsAuthResponses: cache.New(time.Hour, 2*time.Hour), + AISessionManager: NewAISessionManager(lpNode, AISessionManagerTTL), } if lpNode.NodeType == core.BroadcasterNode && httpIngest { opts.HttpMux.HandleFunc("/live/", ls.HandlePush) From e1227575225fb229c0a4f3e07a909f989ebb0d87 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Wed, 13 Mar 2024 13:46:25 +0000 Subject: [PATCH 051/203] cmd: Use abs path for default model dir --- cmd/livepeer/starter/starter.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index 30386b10f..346eea6d0 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -512,7 +512,12 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { modelsDir := *cfg.AIModelsDir if modelsDir == "" { - modelsDir = path.Join(*cfg.Datadir, "models") + var err error + modelsDir, err = filepath.Abs(path.Join(*cfg.Datadir, "models")) + if err != nil { + glog.Error("Error creating absolute path for models dir: %v", modelsDir) + return + } } if err := os.MkdirAll(modelsDir, 0755); err != nil { From a2b194edf19377212d5831f71d6770de155ac495 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Wed, 13 Mar 2024 13:47:14 +0000 Subject: [PATCH 052/203] server: Remove check for deprecated seg data profiles This should no longer be needed because the field is deprecated --- server/rpc.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/server/rpc.go b/server/rpc.go index ae59e4cc8..7f6704460 100644 --- a/server/rpc.go +++ b/server/rpc.go @@ -496,8 +496,6 @@ func coreSegMetadata(segData *net.SegData) (*core.SegTranscodingMetadata, error) profiles, err = makeFfmpegVideoProfiles(segData.FullProfiles2) } else if len(segData.FullProfiles) > 0 { profiles, err = makeFfmpegVideoProfiles(segData.FullProfiles) - } else if len(segData.Profiles) > 0 { - profiles, err = common.BytesToVideoProfile(segData.Profiles) } if err != nil { glog.Error("Unable to deserialize profiles ", err) From 13f56379f33361c856d868a58a7f40f6f7c1860f Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Wed, 13 Mar 2024 15:27:16 +0000 Subject: [PATCH 053/203] server: Payments for text-to-image --- server/ai_http.go | 54 +++++++++++++++++++++++++++++++ server/ai_process.go | 76 ++++++++++++++++++++++++++++++++++++++++++-- server/ai_session.go | 63 +++++++++++++++++++++++++++++++++--- 3 files changed, 186 insertions(+), 7 deletions(-) diff --git a/server/ai_http.go b/server/ai_http.go index 0e098eed0..a8dd40205 100644 --- a/server/ai_http.go +++ b/server/ai_http.go @@ -4,12 +4,14 @@ import ( "context" "encoding/json" "net/http" + "strconv" "time" "github.com/getkin/kin-openapi/openapi3filter" "github.com/livepeer/ai-worker/worker" "github.com/livepeer/go-livepeer/clog" "github.com/livepeer/go-livepeer/common" + "github.com/livepeer/go-livepeer/core" middleware "github.com/oapi-codegen/nethttp-middleware" "github.com/oapi-codegen/runtime" ) @@ -43,9 +45,24 @@ func startAIServer(lp lphttp) error { func (h *lphttp) TextToImage() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + orch := h.orchestrator + remoteAddr := getRemoteAddr(r) ctx := clog.AddVal(r.Context(), clog.ClientIP, remoteAddr) + payment, err := getPayment(r.Header.Get(paymentHeader)) + if err != nil { + respondWithError(w, err.Error(), http.StatusPaymentRequired) + return + } + sender := getPaymentSender(payment) + + _, ctx, err = verifySegCreds(ctx, orch, r.Header.Get(segmentHeader), sender) + if err != nil { + respondWithError(w, err.Error(), http.StatusForbidden) + return + } + var req worker.TextToImageJSONRequestBody if err := json.NewDecoder(r.Body).Decode(&req); err != nil { respondWithError(w, err.Error(), http.StatusBadRequest) @@ -54,6 +71,28 @@ func (h *lphttp) TextToImage() http.Handler { clog.V(common.VERBOSE).Infof(ctx, "Received TextToImage request prompt=%v model_id=%v", req.Prompt, *req.ModelId) + manifestID := core.ManifestID(strconv.Itoa(int(core.Capability_TextToImage)) + "_" + *req.ModelId) + + // Known limitation: + // This call will set a fixed price for all requests in a session identified by a manifestID. + // Since all requests for a capability + modelID are treated as "session" with a single manifestID, all + // requests for a capability + modelID will get the same fixed price for as long as the orch is running + if err := h.orchestrator.ProcessPayment(ctx, payment, manifestID); err != nil { + respondWithError(w, err.Error(), http.StatusBadRequest) + return + } + + if payment.GetExpectedPrice().GetPricePerUnit() > 0 && !orch.SufficientBalance(sender, manifestID) { + respondWithError(w, "Insufficient balance", http.StatusBadRequest) + return + } + + // At the moment, this method does not return a new OrchestratorInfo with updated ticket params + price with + // extended expiry because the response format does not include such a field. As a result, the broadcaster + // might encounter an expiration error for ticket params + price when it is using an old OrchestratorInfo returned + // by the orch during discovery. In that scenario, the broadcaster can use a GetOrchestrator() RPC call to get a + // a new OrchestratorInfo before submitting a request. + start := time.Now() resp, err := h.orchestrator.TextToImage(r.Context(), req) if err != nil { @@ -64,6 +103,21 @@ func (h *lphttp) TextToImage() http.Handler { took := time.Since(start) clog.Infof(ctx, "Processed TextToImage request prompt=%v model_id=%v took=%v", req.Prompt, *req.ModelId, took) + // TODO: The orchestrator should require the broadcaster to always specify a height and width + height := int64(512) + if req.Height != nil { + height = int64(*req.Height) + } + width := int64(512) + if req.Width != nil { + width = int64(*req.Width) + } + + // If the # of inference/denoising steps becomes configurable, a possible updated formula could be height * width * steps + // If additional parameters that influence compute cost become configurable, then the formula should be reconsidered + outPixels := height * width + h.orchestrator.DebitFees(sender, manifestID, payment.GetExpectedPrice(), outPixels) + w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _ = json.NewEncoder(w).Encode(resp) diff --git a/server/ai_process.go b/server/ai_process.go index 19a7b7cdb..cbb98c28a 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -7,6 +7,8 @@ import ( "encoding/json" "errors" "io" + "math/big" + "net/http" "path/filepath" "sort" "strings" @@ -17,6 +19,7 @@ import ( "github.com/livepeer/go-livepeer/core" "github.com/livepeer/go-livepeer/net" "github.com/livepeer/go-tools/drivers" + "github.com/livepeer/lpms/stream" ) const maxProcessingRetries = 4 @@ -90,9 +93,12 @@ func processTextToImage(ctx context.Context, params aiRequestParams, req worker. tries := 0 for tries < maxProcessingRetries { + tries++ + sess, err := params.sessManager.Select(ctx, core.Capability_TextToImage, modelID) if err != nil { - return nil, err + clog.Infof(ctx, "Error selecting TextToImage session err=%v", err) + continue } if sess == nil { @@ -109,7 +115,6 @@ func processTextToImage(ctx context.Context, params aiRequestParams, req worker. params.sessManager.Remove(ctx, sess) - tries++ } if resp == nil { @@ -143,10 +148,64 @@ func submitTextToImage(ctx context.Context, params aiRequestParams, sess *AISess return nil, err } - resp, err := client.TextToImageWithResponse(ctx, req) + // genSegCreds expects a stream.HLSSegment so in order to reuse it here we pass a dummy object + segCreds, err := genSegCreds(sess.BroadcastSession, &stream.HLSSegment{}, nil, false) + if err != nil { + return nil, err + } + + priceInfo, err := common.RatPriceInfo(sess.OrchestratorInfo.GetPriceInfo()) + if err != nil { + return nil, err + } + + if req.Height == nil { + req.Height = new(int) + *req.Height = 512 + } + if req.Width == nil { + req.Width = new(int) + *req.Width = 512 + } + + // If the # of inference/denoising steps becomes configurable, a possible updated formula could be height * width * steps + // If additional parameters that influence compute cost become configurable, then the formula should be reconsidered + outPixels := int64(*req.Height) * int64(*req.Height) + fee, err := estimateAIFee(outPixels, priceInfo) + if err != nil { + return nil, err + } + + balUpdate, err := newBalanceUpdate(sess.BroadcastSession, fee) if err != nil { return nil, err } + defer completeBalanceUpdate(sess.BroadcastSession, balUpdate) + + payment, err := genPayment(ctx, sess.BroadcastSession, balUpdate.NumTickets) + if err != nil { + return nil, err + } + + // As soon as the request is sent to the orch consider the balance update's credit as spent + balUpdate.Status = CreditSpent + + setHeaders := func(_ context.Context, req *http.Request) error { + req.Header.Set(segmentHeader, segCreds) + req.Header.Set(paymentHeader, payment) + return nil + } + + resp, err := client.TextToImageWithResponse(ctx, req, setHeaders) + if err != nil { + return nil, err + } + + // We treat a response as "receiving change" where the change is the difference between the credit and debit for the update + balUpdate.Status = ReceivedChange + if priceInfo != nil { + balUpdate.Debit = fee + } if resp.JSON200 == nil { // TODO: Replace trim newline with better error spec from O @@ -347,3 +406,14 @@ func submitImageToVideo(ctx context.Context, url string, req worker.ImageToVideo return &res, nil } + +func estimateAIFee(outPixels int64, priceInfo *big.Rat) (*big.Rat, error) { + if priceInfo == nil { + return nil, nil + } + + fee := new(big.Rat).SetInt64(outPixels) + fee.Mul(fee, priceInfo) + + return fee, nil +} diff --git a/server/ai_session.go b/server/ai_session.go index fc4c347fd..7a5930ce5 100644 --- a/server/ai_session.go +++ b/server/ai_session.go @@ -2,6 +2,7 @@ package server import ( "context" + "math" "strconv" "sync" "time" @@ -25,6 +26,7 @@ type AISessionPool struct { selector BroadcastSessionsSelector sessMap map[string]*BroadcastSession suspender *suspender + mu sync.RWMutex } func NewAISessionPool(selector BroadcastSessionsSelector, suspender *suspender) *AISessionPool { @@ -32,10 +34,14 @@ func NewAISessionPool(selector BroadcastSessionsSelector, suspender *suspender) selector: selector, sessMap: make(map[string]*BroadcastSession), suspender: suspender, + mu: sync.RWMutex{}, } } func (pool *AISessionPool) Select(ctx context.Context) *BroadcastSession { + pool.mu.Lock() + defer pool.mu.Unlock() + for { sess := pool.selector.Select(ctx) if sess == nil { @@ -52,6 +58,9 @@ func (pool *AISessionPool) Select(ctx context.Context) *BroadcastSession { } func (pool *AISessionPool) Complete(sess *BroadcastSession) { + pool.mu.Lock() + defer pool.mu.Unlock() + existingSess, ok := pool.sessMap[sess.Transcoder()] if !ok { // If the session is not tracked by sessMap, skip returning it to the selector @@ -68,6 +77,9 @@ func (pool *AISessionPool) Complete(sess *BroadcastSession) { } func (pool *AISessionPool) Add(sessions []*BroadcastSession) { + pool.mu.Lock() + defer pool.mu.Unlock() + // If we try to add new sessions to the pool the suspender // should treat this as a refresh pool.suspender.signalRefresh() @@ -87,6 +99,9 @@ func (pool *AISessionPool) Add(sessions []*BroadcastSession) { } func (pool *AISessionPool) Remove(sess *BroadcastSession) { + pool.mu.Lock() + defer pool.mu.Unlock() + delete(pool.sessMap, sess.Transcoder()) // Magic number for now @@ -96,6 +111,13 @@ func (pool *AISessionPool) Remove(sess *BroadcastSession) { pool.suspender.suspend(sess.Transcoder(), penalty) } +func (pool *AISessionPool) Size() int { + pool.mu.RLock() + defer pool.mu.RUnlock() + + return len(pool.sessMap) +} + type AISessionSelector struct { // Pool of sessions with orchs that have the requested model warm warmPool *AISessionPool @@ -145,7 +167,24 @@ func NewAISessionSelector(cap core.Capability, modelID string, node *core.Livepe } func (sel *AISessionSelector) Select(ctx context.Context) *AISession { - if time.Now().After(sel.lastRefreshTime.Add(sel.ttl)) { + shouldRefreshSelector := func() bool { + // Refresh if the # of sessions across warm and cold pools falls below the smaller of the maxRefreshSessionsThreshold and + // 1/2 the total # of orchs that can be queried during discovery + discoveryPoolSize := sel.node.OrchestratorPool.Size() + if sel.warmPool.Size()+sel.coldPool.Size() < int(math.Min(maxRefreshSessionsThreshold, math.Ceil(float64(discoveryPoolSize)/2.0))) { + return true + } + + // Refresh if the selector has expired + if time.Now().After(sel.lastRefreshTime.Add(sel.ttl)) { + return true + } + + return false + } + + if shouldRefreshSelector() { + // Should this call be in a goroutine so the refresh can happen in the background? if err := sel.Refresh(ctx); err != nil { clog.Infof(ctx, "Error refreshing AISessionSelector err=%v", err) } @@ -249,7 +288,7 @@ func (sel *AISessionSelector) getSessions(ctx context.Context) ([]*BroadcastSess type AISessionManager struct { node *core.LivepeerNode selectors map[string]*AISessionSelector - mu *sync.Mutex + mu sync.Mutex ttl time.Duration } @@ -257,7 +296,7 @@ func NewAISessionManager(node *core.LivepeerNode, ttl time.Duration) *AISessionM return &AISessionManager{ node: node, selectors: make(map[string]*AISessionSelector), - mu: &sync.Mutex{}, + mu: sync.Mutex{}, ttl: ttl, } } @@ -268,7 +307,23 @@ func (c *AISessionManager) Select(ctx context.Context, cap core.Capability, mode return nil, err } - return sel.Select(ctx), nil + sess := sel.Select(ctx) + if sess == nil { + return nil, nil + } + + shouldRefresh, err := shouldRefreshSession(ctx, sess.BroadcastSession) + if err != nil { + return nil, err + } + + if shouldRefresh { + if err := refreshSession(ctx, sess.BroadcastSession); err != nil { + return nil, err + } + } + + return sess, nil } func (c *AISessionManager) Remove(ctx context.Context, sess *AISession) error { From a77f0a0cf1e623f54e76beb28e21f714fc0ccdb5 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Thu, 14 Mar 2024 20:13:04 +0000 Subject: [PATCH 054/203] server: Payments + session manager for all AI caps --- server/ai_http.go | 218 +++++++++++++---------- server/ai_mediaserver.go | 10 +- server/ai_process.go | 374 ++++++++++++++++++--------------------- 3 files changed, 308 insertions(+), 294 deletions(-) diff --git a/server/ai_http.go b/server/ai_http.go index a8dd40205..31368edc0 100644 --- a/server/ai_http.go +++ b/server/ai_http.go @@ -3,6 +3,7 @@ package server import ( "context" "encoding/json" + "image" "net/http" "strconv" "time" @@ -50,82 +51,20 @@ func (h *lphttp) TextToImage() http.Handler { remoteAddr := getRemoteAddr(r) ctx := clog.AddVal(r.Context(), clog.ClientIP, remoteAddr) - payment, err := getPayment(r.Header.Get(paymentHeader)) - if err != nil { - respondWithError(w, err.Error(), http.StatusPaymentRequired) - return - } - sender := getPaymentSender(payment) - - _, ctx, err = verifySegCreds(ctx, orch, r.Header.Get(segmentHeader), sender) - if err != nil { - respondWithError(w, err.Error(), http.StatusForbidden) - return - } - var req worker.TextToImageJSONRequestBody if err := json.NewDecoder(r.Body).Decode(&req); err != nil { respondWithError(w, err.Error(), http.StatusBadRequest) return } - clog.V(common.VERBOSE).Infof(ctx, "Received TextToImage request prompt=%v model_id=%v", req.Prompt, *req.ModelId) - - manifestID := core.ManifestID(strconv.Itoa(int(core.Capability_TextToImage)) + "_" + *req.ModelId) - - // Known limitation: - // This call will set a fixed price for all requests in a session identified by a manifestID. - // Since all requests for a capability + modelID are treated as "session" with a single manifestID, all - // requests for a capability + modelID will get the same fixed price for as long as the orch is running - if err := h.orchestrator.ProcessPayment(ctx, payment, manifestID); err != nil { - respondWithError(w, err.Error(), http.StatusBadRequest) - return - } - - if payment.GetExpectedPrice().GetPricePerUnit() > 0 && !orch.SufficientBalance(sender, manifestID) { - respondWithError(w, "Insufficient balance", http.StatusBadRequest) - return - } - - // At the moment, this method does not return a new OrchestratorInfo with updated ticket params + price with - // extended expiry because the response format does not include such a field. As a result, the broadcaster - // might encounter an expiration error for ticket params + price when it is using an old OrchestratorInfo returned - // by the orch during discovery. In that scenario, the broadcaster can use a GetOrchestrator() RPC call to get a - // a new OrchestratorInfo before submitting a request. - - start := time.Now() - resp, err := h.orchestrator.TextToImage(r.Context(), req) - if err != nil { - respondWithError(w, err.Error(), http.StatusInternalServerError) - return - } - - took := time.Since(start) - clog.Infof(ctx, "Processed TextToImage request prompt=%v model_id=%v took=%v", req.Prompt, *req.ModelId, took) - - // TODO: The orchestrator should require the broadcaster to always specify a height and width - height := int64(512) - if req.Height != nil { - height = int64(*req.Height) - } - width := int64(512) - if req.Width != nil { - width = int64(*req.Width) - } - - // If the # of inference/denoising steps becomes configurable, a possible updated formula could be height * width * steps - // If additional parameters that influence compute cost become configurable, then the formula should be reconsidered - outPixels := height * width - h.orchestrator.DebitFees(sender, manifestID, payment.GetExpectedPrice(), outPixels) - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - _ = json.NewEncoder(w).Encode(resp) + handleAIRequest(ctx, w, r, orch, req) }) } func (h *lphttp) ImageToImage() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + orch := h.orchestrator + remoteAddr := getRemoteAddr(r) ctx := clog.AddVal(r.Context(), clog.ClientIP, remoteAddr) @@ -141,26 +80,14 @@ func (h *lphttp) ImageToImage() http.Handler { return } - clog.V(common.VERBOSE).Infof(r.Context(), "Received ImageToImage request imageSize=%v prompt=%v model_id=%v", req.Image.FileSize(), req.Prompt, *req.ModelId) - - start := time.Now() - resp, err := h.orchestrator.ImageToImage(r.Context(), req) - if err != nil { - respondWithError(w, err.Error(), http.StatusInternalServerError) - return - } - - took := time.Since(start) - clog.Infof(ctx, "Processed ImageToImage request imageSize=%v prompt=%v model_id=%v took=%v", req.Image.FileSize(), req.Prompt, *req.ModelId, took) - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - _ = json.NewEncoder(w).Encode(resp) + handleAIRequest(ctx, w, r, orch, req) }) } func (h *lphttp) ImageToVideo() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + orch := h.orchestrator + remoteAddr := getRemoteAddr(r) ctx := clog.AddVal(r.Context(), clog.ClientIP, remoteAddr) @@ -176,20 +103,131 @@ func (h *lphttp) ImageToVideo() http.Handler { return } - clog.V(common.VERBOSE).Infof(ctx, "Received ImageToVideo request imageSize=%v model_id=%v", req.Image.FileSize(), *req.ModelId) + handleAIRequest(ctx, w, r, orch, req) + }) +} + +func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request, orch Orchestrator, req interface{}) { + payment, err := getPayment(r.Header.Get(paymentHeader)) + if err != nil { + respondWithError(w, err.Error(), http.StatusPaymentRequired) + return + } + sender := getPaymentSender(payment) + + _, ctx, err = verifySegCreds(ctx, orch, r.Header.Get(segmentHeader), sender) + if err != nil { + respondWithError(w, err.Error(), http.StatusForbidden) + return + } + + var cap core.Capability + var modelID string + var submitFn func(context.Context) (*worker.ImageResponse, error) + var outPixels int64 + + switch v := req.(type) { + case worker.TextToImageJSONRequestBody: + cap = core.Capability_TextToImage + modelID = *v.ModelId + submitFn = func(ctx context.Context) (*worker.ImageResponse, error) { + return orch.TextToImage(ctx, v) + } + + // TODO: The orchestrator should require the broadcaster to always specify a height and width + height := int64(512) + if v.Height != nil { + height = int64(*v.Height) + } + width := int64(512) + if v.Width != nil { + width = int64(*v.Width) + } + + outPixels = height * width + case worker.ImageToImageMultipartRequestBody: + cap = core.Capability_ImageToImage + modelID = *v.ModelId + submitFn = func(ctx context.Context) (*worker.ImageResponse, error) { + return orch.ImageToImage(ctx, v) + } - start := time.Now() - resp, err := h.orchestrator.ImageToVideo(ctx, req) + imageRdr, err := v.Image.Reader() if err != nil { - respondWithError(w, err.Error(), http.StatusInternalServerError) + respondWithError(w, err.Error(), http.StatusBadRequest) return } + config, _, err := image.DecodeConfig(imageRdr) + if err != nil { + respondWithError(w, err.Error(), http.StatusBadRequest) + return + } + outPixels = int64(config.Height) * int64(config.Width) + case worker.ImageToVideoMultipartRequestBody: + cap = core.Capability_ImageToVideo + modelID = *v.ModelId + submitFn = func(ctx context.Context) (*worker.ImageResponse, error) { + return orch.ImageToVideo(ctx, v) + } - took := time.Since(start) - clog.Infof(ctx, "Processed ImageToVideo request imageSize=%v model_id=%v took=%v", req.Image.FileSize(), *req.ModelId, took) + // TODO: The orchestrator should require the broadcaster to always specify a height and width + height := int64(576) + if v.Height != nil { + height = int64(*v.Height) + } + width := int64(1024) + if v.Width != nil { + width = int64(*v.Width) + } - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - _ = json.NewEncoder(w).Encode(resp) - }) + outPixels = height * width + default: + respondWithError(w, "Unknown request type", http.StatusBadRequest) + return + } + + requestID := string(core.RandomManifestID()) + + clog.V(common.VERBOSE).Infof(ctx, "Received request id=%v cap=%v modelID=%v", requestID, cap, modelID) + + manifestID := core.ManifestID(strconv.Itoa(int(cap)) + "_" + modelID) + + // Known limitation: + // This call will set a fixed price for all requests in a session identified by a manifestID. + // Since all requests for a capability + modelID are treated as "session" with a single manifestID, all + // requests for a capability + modelID will get the same fixed price for as long as the orch is running + if err := orch.ProcessPayment(ctx, payment, manifestID); err != nil { + respondWithError(w, err.Error(), http.StatusBadRequest) + return + } + + if payment.GetExpectedPrice().GetPricePerUnit() > 0 && !orch.SufficientBalance(sender, manifestID) { + respondWithError(w, "Insufficient balance", http.StatusBadRequest) + return + } + + // Note: At the moment, we do not return a new OrchestratorInfo with updated ticket params + price with + // extended expiry because the response format does not include such a field. As a result, the broadcaster + // might encounter an expiration error for ticket params + price when it is using an old OrchestratorInfo returned + // by the orch during discovery. In that scenario, the broadcaster can use a GetOrchestrator() RPC call to get a + // a new OrchestratorInfo before submitting a request. + + start := time.Now() + resp, err := submitFn(ctx) + if err != nil { + respondWithError(w, err.Error(), http.StatusInternalServerError) + return + } + + took := time.Since(start) + clog.Infof(ctx, "Processed request id=%v cap=%v modelID=%v took=%v", requestID, cap, modelID, took) + + // At the moment, outPixels is expected to just be height * width + // If the # of inference/denoising steps becomes configurable, a possible updated formula could be height * width * steps + // If additional parameters that influence compute cost become configurable, then the formula should be reconsidered + orch.DebitFees(sender, manifestID, payment.GetExpectedPrice(), outPixels) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(resp) } diff --git a/server/ai_mediaserver.go b/server/ai_mediaserver.go index ad0e8756a..ebaa54f17 100644 --- a/server/ai_mediaserver.go +++ b/server/ai_mediaserver.go @@ -135,8 +135,9 @@ func (ls *LivepeerServer) ImageToImage() http.Handler { clog.V(common.VERBOSE).Infof(ctx, "Received ImageToImage request imageSize=%v prompt=%v model_id=%v", req.Image.FileSize(), req.Prompt, *req.ModelId) params := aiRequestParams{ - node: ls.LivepeerNode, - os: drivers.NodeStorage.NewSession(string(core.RandomManifestID())), + node: ls.LivepeerNode, + os: drivers.NodeStorage.NewSession(string(core.RandomManifestID())), + sessManager: ls.AISessionManager, } start := time.Now() @@ -188,8 +189,9 @@ func (ls *LivepeerServer) ImageToVideo() http.Handler { clog.V(common.VERBOSE).Infof(ctx, "Received ImageToVideo request imageSize=%v model_id=%v async=%v", req.Image.FileSize(), *req.ModelId, async) params := aiRequestParams{ - node: ls.LivepeerNode, - os: drivers.NodeStorage.NewSession(requestID), + node: ls.LivepeerNode, + os: drivers.NodeStorage.NewSession(requestID), + sessManager: ls.AISessionManager, } if !async { diff --git a/server/ai_process.go b/server/ai_process.go index cbb98c28a..996ca8cad 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -6,18 +6,17 @@ import ( "context" "encoding/json" "errors" + "image" "io" "math/big" "net/http" "path/filepath" - "sort" "strings" "github.com/livepeer/ai-worker/worker" "github.com/livepeer/go-livepeer/clog" "github.com/livepeer/go-livepeer/common" "github.com/livepeer/go-livepeer/core" - "github.com/livepeer/go-livepeer/net" "github.com/livepeer/go-tools/drivers" "github.com/livepeer/lpms/stream" ) @@ -41,86 +40,12 @@ type aiRequestParams struct { sessManager *AISessionManager } -func getOrchestratorsForAIRequest(ctx context.Context, params aiRequestParams, cap core.Capability, modelID string) ([]*net.OrchestratorInfo, error) { - // No warm constraints applied here because we don't want to filter out orchs based on warm criteria at discovery time - // Instead, we want all orchs that support the model and then will prioritize orchs that have a warm model at selection time - constraints := map[core.Capability]*core.Constraints{ - cap: { - Models: map[string]*core.ModelConstraint{ - modelID: { - Warm: false, - }, - }, - }, - } - caps := core.NewCapabilitiesWithConstraints(append(core.DefaultCapabilities(), cap), nil, constraints) - - // Set numOrchs to the pool size so that discovery tries to find maximum # of compatible orchs within a timeout - numOrchs := params.node.OrchestratorPool.Size() - orchDesc, err := params.node.OrchestratorPool.GetOrchestrators(ctx, numOrchs, newSuspender(), caps, common.ScoreAtLeast(0)) +func processTextToImage(ctx context.Context, params aiRequestParams, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) { + resp, err := processAIRequest(ctx, params, req) if err != nil { return nil, err } - orchInfos := orchDesc.GetRemoteInfos() - - // Sort and prioritize orchs that have warm model - sort.Slice(orchInfos, func(i, j int) bool { - iConstraints, ok := orchInfos[i].Capabilities.Constraints[uint32(cap)] - if !ok { - return false - } - - iModelConstraint, ok := iConstraints.Models[modelID] - if !ok { - return false - } - - // If warm i goes before j, else j goes before i - return iModelConstraint.Warm - }) - - return orchInfos, nil -} - -func processTextToImage(ctx context.Context, params aiRequestParams, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) { - modelID := defaultTextToImageModelID - if req.ModelId != nil { - modelID = *req.ModelId - } - - var resp *worker.ImageResponse - - tries := 0 - for tries < maxProcessingRetries { - tries++ - - sess, err := params.sessManager.Select(ctx, core.Capability_TextToImage, modelID) - if err != nil { - clog.Infof(ctx, "Error selecting TextToImage session err=%v", err) - continue - } - - if sess == nil { - break - } - - resp, err = submitTextToImage(ctx, params, sess, req) - if err == nil { - params.sessManager.Complete(ctx, sess) - break - } - - clog.Infof(ctx, "Error submitting TextToImage request try=%v orch=%v err=%v", tries, sess.Transcoder(), err) - - params.sessManager.Remove(ctx, sess) - - } - - if resp == nil { - return nil, &ServiceUnavailableError{err: errors.New("no orchestrators available")} - } - newMedia := make([]worker.Media, len(resp.Images)) for i, media := range resp.Images { var data bytes.Buffer @@ -148,17 +73,6 @@ func submitTextToImage(ctx context.Context, params aiRequestParams, sess *AISess return nil, err } - // genSegCreds expects a stream.HLSSegment so in order to reuse it here we pass a dummy object - segCreds, err := genSegCreds(sess.BroadcastSession, &stream.HLSSegment{}, nil, false) - if err != nil { - return nil, err - } - - priceInfo, err := common.RatPriceInfo(sess.OrchestratorInfo.GetPriceInfo()) - if err != nil { - return nil, err - } - if req.Height == nil { req.Height = new(int) *req.Height = 512 @@ -168,96 +82,37 @@ func submitTextToImage(ctx context.Context, params aiRequestParams, sess *AISess *req.Width = 512 } - // If the # of inference/denoising steps becomes configurable, a possible updated formula could be height * width * steps - // If additional parameters that influence compute cost become configurable, then the formula should be reconsidered - outPixels := int64(*req.Height) * int64(*req.Height) - fee, err := estimateAIFee(outPixels, priceInfo) - if err != nil { - return nil, err - } - - balUpdate, err := newBalanceUpdate(sess.BroadcastSession, fee) + outPixels := int64(*req.Height) * int64(*req.Width) + setHeaders, balUpdate, err := prepareAIPayment(ctx, sess, outPixels) if err != nil { return nil, err } defer completeBalanceUpdate(sess.BroadcastSession, balUpdate) - payment, err := genPayment(ctx, sess.BroadcastSession, balUpdate.NumTickets) - if err != nil { - return nil, err - } - - // As soon as the request is sent to the orch consider the balance update's credit as spent - balUpdate.Status = CreditSpent - - setHeaders := func(_ context.Context, req *http.Request) error { - req.Header.Set(segmentHeader, segCreds) - req.Header.Set(paymentHeader, payment) - return nil - } - resp, err := client.TextToImageWithResponse(ctx, req, setHeaders) if err != nil { return nil, err } - // We treat a response as "receiving change" where the change is the difference between the credit and debit for the update - balUpdate.Status = ReceivedChange - if priceInfo != nil { - balUpdate.Debit = fee - } - if resp.JSON200 == nil { // TODO: Replace trim newline with better error spec from O return nil, errors.New(strings.TrimSuffix(string(resp.Body), "\n")) } + // We treat a response as "receiving change" where the change is the difference between the credit and debit for the update + if balUpdate != nil { + balUpdate.Status = ReceivedChange + } + return resp.JSON200, nil } func processImageToImage(ctx context.Context, params aiRequestParams, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { - modelID := defaultImageToImageModelID - if req.ModelId != nil { - modelID = *req.ModelId - } - - orchInfos, err := getOrchestratorsForAIRequest(ctx, params, core.Capability_ImageToImage, modelID) + resp, err := processAIRequest(ctx, params, req) if err != nil { return nil, err } - if len(orchInfos) == 0 { - return nil, &ServiceUnavailableError{err: errors.New("no orchestrators available")} - } - - var resp *worker.ImageResponse - - // Round robin up to maxProcessingRetries times - orchIdx := 0 - tries := 0 - for tries < maxProcessingRetries { - orchUrl := orchInfos[orchIdx].Transcoder - - var err error - resp, err = submitImageToImage(ctx, orchUrl, req) - if err == nil { - break - } - - clog.Infof(ctx, "Error submitting ImageToImage request try=%v orch=%v err=%v", tries, orchUrl, err) - - tries++ - orchIdx++ - // Wrap back around - if orchIdx >= len(orchInfos) { - orchIdx = 0 - } - } - - if resp == nil { - return nil, &ServiceUnavailableError{err: errors.New("no orchestrators available")} - } - newMedia := make([]worker.Media, len(resp.Images)) for i, media := range resp.Images { var data bytes.Buffer @@ -279,72 +134,56 @@ func processImageToImage(ctx context.Context, params aiRequestParams, req worker return resp, nil } -func submitImageToImage(ctx context.Context, url string, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { +func submitImageToImage(ctx context.Context, params aiRequestParams, sess *AISession, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { var buf bytes.Buffer mw, err := worker.NewImageToImageMultipartWriter(&buf, req) if err != nil { return nil, err } - client, err := worker.NewClientWithResponses(url, worker.WithHTTPClient(httpClient)) + client, err := worker.NewClientWithResponses(sess.Transcoder(), worker.WithHTTPClient(httpClient)) if err != nil { return nil, err } - resp, err := client.ImageToImageWithBodyWithResponse(ctx, mw.FormDataContentType(), &buf) + imageRdr, err := req.Image.Reader() if err != nil { return nil, err } - - if resp.JSON200 == nil { - // TODO: Replace trim newline with better error spec from O - return nil, errors.New(strings.TrimSuffix(string(resp.Body), "\n")) + config, _, err := image.DecodeConfig(imageRdr) + if err != nil { + return nil, err } + outPixels := int64(config.Height) * int64(config.Width) - return resp.JSON200, nil -} - -func processImageToVideo(ctx context.Context, params aiRequestParams, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { - modelID := defaultImageToVideoModelID - if req.ModelId != nil { - modelID = *req.ModelId + setHeaders, balUpdate, err := prepareAIPayment(ctx, sess, outPixels) + if err != nil { + return nil, err } + defer completeBalanceUpdate(sess.BroadcastSession, balUpdate) - orchInfos, err := getOrchestratorsForAIRequest(ctx, params, core.Capability_ImageToVideo, modelID) + resp, err := client.ImageToImageWithBodyWithResponse(ctx, mw.FormDataContentType(), &buf, setHeaders) if err != nil { return nil, err } - if len(orchInfos) == 0 { - return nil, &ServiceUnavailableError{err: errors.New("no orchestrators available")} + if resp.JSON200 == nil { + // TODO: Replace trim newline with better error spec from O + return nil, errors.New(strings.TrimSuffix(string(resp.Body), "\n")) } - var resp *worker.ImageResponse - - // Round robin up to maxProcessingRetries times - orchIdx := 0 - tries := 0 - for tries < maxProcessingRetries { - orchUrl := orchInfos[orchIdx].Transcoder - - var err error - resp, err = submitImageToVideo(ctx, orchUrl, req) - if err == nil { - break - } - - clog.Infof(ctx, "Error submitting ImageToVideo request try=%v orch=%v err=%v", tries, orchUrl, err) - - tries++ - orchIdx++ - // Wrap back around - if orchIdx >= len(orchInfos) { - orchIdx = 0 - } + // We treat a response as "receiving change" where the change is the difference between the credit and debit for the update + if balUpdate != nil { + balUpdate.Status = ReceivedChange } - if resp == nil { - return nil, &ServiceUnavailableError{err: errors.New("no orchestrators available")} + return resp.JSON200, nil +} + +func processImageToVideo(ctx context.Context, params aiRequestParams, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { + resp, err := processAIRequest(ctx, params, req) + if err != nil { + return nil, err } // HACK: Re-use worker.ImageResponse to return results @@ -372,19 +211,35 @@ func processImageToVideo(ctx context.Context, params aiRequestParams, req worker return resp, nil } -func submitImageToVideo(ctx context.Context, url string, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { +func submitImageToVideo(ctx context.Context, params aiRequestParams, sess *AISession, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { var buf bytes.Buffer mw, err := worker.NewImageToVideoMultipartWriter(&buf, req) if err != nil { return nil, err } - client, err := worker.NewClientWithResponses(url, worker.WithHTTPClient(httpClient)) + client, err := worker.NewClientWithResponses(sess.Transcoder(), worker.WithHTTPClient(httpClient)) if err != nil { return nil, err } - resp, err := client.ImageToVideoWithBody(ctx, mw.FormDataContentType(), &buf) + if req.Height == nil { + req.Height = new(int) + *req.Height = 576 + } + if req.Width == nil { + req.Width = new(int) + *req.Width = 1024 + } + + outPixels := int64(*req.Height) * int64(*req.Width) + setHeaders, balUpdate, err := prepareAIPayment(ctx, sess, outPixels) + if err != nil { + return nil, err + } + defer completeBalanceUpdate(sess.BroadcastSession, balUpdate) + + resp, err := client.ImageToVideoWithBody(ctx, mw.FormDataContentType(), &buf, setHeaders) if err != nil { return nil, err } @@ -399,6 +254,11 @@ func submitImageToVideo(ctx context.Context, url string, req worker.ImageToVideo return nil, errors.New(string(data)) } + // We treat a response as "receiving change" where the change is the difference between the credit and debit for the update + if balUpdate != nil { + balUpdate.Status = ReceivedChange + } + var res worker.ImageResponse if err := json.Unmarshal(data, &res); err != nil { return nil, err @@ -407,6 +267,120 @@ func submitImageToVideo(ctx context.Context, url string, req worker.ImageToVideo return &res, nil } +func processAIRequest(ctx context.Context, params aiRequestParams, req interface{}) (*worker.ImageResponse, error) { + var cap core.Capability + var modelID string + var submitFn func(context.Context, aiRequestParams, *AISession) (*worker.ImageResponse, error) + + switch v := req.(type) { + case worker.TextToImageJSONRequestBody: + cap = core.Capability_TextToImage + modelID = defaultTextToImageModelID + if v.ModelId != nil { + modelID = *v.ModelId + } + submitFn = func(ctx context.Context, params aiRequestParams, sess *AISession) (*worker.ImageResponse, error) { + return submitTextToImage(ctx, params, sess, v) + } + case worker.ImageToImageMultipartRequestBody: + cap = core.Capability_ImageToImage + modelID = defaultImageToImageModelID + if v.ModelId != nil { + modelID = *v.ModelId + } + submitFn = func(ctx context.Context, params aiRequestParams, sess *AISession) (*worker.ImageResponse, error) { + return submitImageToImage(ctx, params, sess, v) + } + case worker.ImageToVideoMultipartRequestBody: + cap = core.Capability_ImageToVideo + modelID = defaultImageToVideoModelID + if v.ModelId != nil { + modelID = *v.ModelId + } + submitFn = func(ctx context.Context, params aiRequestParams, sess *AISession) (*worker.ImageResponse, error) { + return submitImageToVideo(ctx, params, sess, v) + } + default: + return nil, errors.New("unknown AI request type") + } + + var resp *worker.ImageResponse + + tries := 0 + for tries < maxProcessingRetries { + tries++ + + sess, err := params.sessManager.Select(ctx, cap, modelID) + if err != nil { + clog.Infof(ctx, "Error selecting session cap=%v modelID=%v err=%v", cap, modelID, err) + continue + } + + if sess == nil { + break + } + + resp, err = submitFn(ctx, params, sess) + if err == nil { + params.sessManager.Complete(ctx, sess) + break + } + + clog.Infof(ctx, "Error submitting request cap=%v modelID=%v try=%v orch=%v err=%v", cap, modelID, tries, sess.Transcoder(), err) + + params.sessManager.Remove(ctx, sess) + } + + if resp == nil { + return nil, &ServiceUnavailableError{err: errors.New("no orchestrators available")} + } + + return resp, nil +} + +func prepareAIPayment(ctx context.Context, sess *AISession, outPixels int64) (worker.RequestEditorFn, *BalanceUpdate, error) { + // genSegCreds expects a stream.HLSSegment so in order to reuse it here we pass a dummy object + segCreds, err := genSegCreds(sess.BroadcastSession, &stream.HLSSegment{}, nil, false) + if err != nil { + return nil, nil, err + } + + priceInfo, err := common.RatPriceInfo(sess.OrchestratorInfo.GetPriceInfo()) + if err != nil { + return nil, nil, err + } + + // At the moment, outPixels is expected to just be height * width + // If the # of inference/denoising steps becomes configurable, a possible updated formula could be height * width * steps + // If additional parameters that influence compute cost become configurable, then the formula should be reconsidered + fee, err := estimateAIFee(outPixels, priceInfo) + if err != nil { + return nil, nil, err + } + + balUpdate, err := newBalanceUpdate(sess.BroadcastSession, fee) + if err != nil { + return nil, nil, err + } + balUpdate.Debit = fee + + payment, err := genPayment(ctx, sess.BroadcastSession, balUpdate.NumTickets) + if err != nil { + return nil, nil, err + } + + // As soon as the request is sent to the orch consider the balance update's credit as spent + balUpdate.Status = CreditSpent + + setHeaders := func(_ context.Context, req *http.Request) error { + req.Header.Set(segmentHeader, segCreds) + req.Header.Set(paymentHeader, payment) + return nil + } + + return setHeaders, balUpdate, nil +} + func estimateAIFee(outPixels int64, priceInfo *big.Rat) (*big.Rat, error) { if priceInfo == nil { return nil, nil From f8ef935646fd4571b1bc9ef6b828d4bcf3d4231e Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Thu, 14 Mar 2024 20:20:54 +0000 Subject: [PATCH 055/203] multi: Support price per AI cap --- cmd/livepeer/starter/starter.go | 6 + common/types.go | 8 +- core/ai.go | 29 +- core/livepeernode.go | 98 ++++- core/orchestrator.go | 51 ++- discovery/db_discovery.go | 2 +- discovery/discovery.go | 2 +- net/lp_rpc.pb.go | 677 ++++++++++++++++---------------- net/lp_rpc.proto | 3 + server/broadcast.go | 2 +- server/rpc.go | 36 +- 11 files changed, 544 insertions(+), 370 deletions(-) diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index 346eea6d0..79367475b 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -562,6 +562,8 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { } constraints[core.Capability_TextToImage].Models[config.ModelID] = modelConstraint + + n.SetBasePriceForCap("default", core.Capability_TextToImage, config.ModelID, big.NewRat(config.PricePerUnit, config.PixelsPerUnit)) case "image-to-image": _, ok := constraints[core.Capability_ImageToImage] if !ok { @@ -572,6 +574,8 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { } constraints[core.Capability_ImageToImage].Models[config.ModelID] = modelConstraint + + n.SetBasePriceForCap("default", core.Capability_ImageToImage, config.ModelID, big.NewRat(config.PricePerUnit, config.PixelsPerUnit)) case "image-to-video": _, ok := constraints[core.Capability_ImageToVideo] if !ok { @@ -582,6 +586,8 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { } constraints[core.Capability_ImageToVideo].Models[config.ModelID] = modelConstraint + + n.SetBasePriceForCap("default", core.Capability_ImageToVideo, config.ModelID, big.NewRat(config.PricePerUnit, config.PixelsPerUnit)) } } } diff --git a/common/types.go b/common/types.go index 0dc4e7445..3e9800304 100644 --- a/common/types.go +++ b/common/types.go @@ -3,12 +3,13 @@ package common import ( "context" "encoding/json" - ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/livepeer/go-livepeer/net" - "github.com/livepeer/m3u8" "math/big" "net/url" "sync" + + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/livepeer/go-livepeer/net" + "github.com/livepeer/m3u8" ) type RemoteTranscoderInfo struct { @@ -48,6 +49,7 @@ type Broadcaster interface { type CapabilityComparator interface { CompatibleWith(*net.Capabilities) bool LegacyOnly() bool + ToNetCapabilities() *net.Capabilities } const ( diff --git a/core/ai.go b/core/ai.go index 0c8ec041c..384762c3c 100644 --- a/core/ai.go +++ b/core/ai.go @@ -20,11 +20,30 @@ type AI interface { } type AIModelConfig struct { - Pipeline string `json:"pipeline"` - ModelID string `json:"model_id"` - URL string `json:"url,omitempty"` - Token string `json:"token,omitempty"` - Warm bool `json:"warm,omitempty"` + Pipeline string `json:"pipeline"` + ModelID string `json:"model_id"` + URL string `json:"url,omitempty"` + Token string `json:"token,omitempty"` + Warm bool `json:"warm,omitempty"` + PricePerUnit int64 `json:"price_per_unit,omitempty"` + PixelsPerUnit int64 `json:"pixels_per_unit,omitempty"` +} + +func (config *AIModelConfig) UnmarshalJSON(data []byte) error { + // Custom type to avoid recursive calls to UnmarshalJSON + type AIModelConfigAlias AIModelConfig + // Set default values for fields + defaultConfig := &AIModelConfigAlias{ + PixelsPerUnit: 1, + } + + if err := json.Unmarshal(data, defaultConfig); err != nil { + return err + } + + *config = AIModelConfig(*defaultConfig) + + return nil } func ParseAIModelConfigs(config string) ([]AIModelConfig, error) { diff --git a/core/livepeernode.go b/core/livepeernode.go index f4391b409..e94c50af2 100644 --- a/core/livepeernode.go +++ b/core/livepeernode.go @@ -63,6 +63,49 @@ func (t NodeType) String() string { return str } +type CapabilityPriceMenu struct { + modelPrices map[string]*big.Rat +} + +func NewCapabilityPriceMenu() CapabilityPriceMenu { + return CapabilityPriceMenu{ + modelPrices: make(map[string]*big.Rat), + } +} + +func (m CapabilityPriceMenu) SetPriceForModelID(modelID string, price *big.Rat) { + m.modelPrices[modelID] = price +} + +func (m CapabilityPriceMenu) PriceForModelID(modelID string) *big.Rat { + return m.modelPrices[modelID] +} + +type CapabilityPrices map[Capability]CapabilityPriceMenu + +func NewCapabilityPrices() CapabilityPrices { + return make(map[Capability]CapabilityPriceMenu) +} + +func (cp CapabilityPrices) SetPriceForModelID(cap Capability, modelID string, price *big.Rat) { + menu, ok := cp[cap] + if !ok { + menu = NewCapabilityPriceMenu() + cp[cap] = menu + } + + menu.SetPriceForModelID(modelID, price) +} + +func (cp CapabilityPrices) PriceForModelID(cap Capability, modelID string) *big.Rat { + menu, ok := cp[cap] + if !ok { + return nil + } + + return menu.PriceForModelID(modelID) +} + // LivepeerNode handles videos going in and coming out of the Livepeer network. type LivepeerNode struct { @@ -96,25 +139,27 @@ type LivepeerNode struct { StorageConfigs map[string]*transcodeConfig storageMutex *sync.RWMutex // Transcoder private fields - priceInfo map[string]*big.Rat - serviceURI url.URL - segmentMutex *sync.RWMutex + priceInfo map[string]*big.Rat + priceInfoForCaps map[string]CapabilityPrices + serviceURI url.URL + segmentMutex *sync.RWMutex } // NewLivepeerNode creates a new Livepeer Node. Eth can be nil. func NewLivepeerNode(e eth.LivepeerEthClient, wd string, dbh *common.DB) (*LivepeerNode, error) { rand.Seed(time.Now().UnixNano()) return &LivepeerNode{ - Eth: e, - WorkDir: wd, - Database: dbh, - AutoAdjustPrice: true, - SegmentChans: make(map[ManifestID]SegmentChan), - segmentMutex: &sync.RWMutex{}, - Capabilities: &Capabilities{capacities: map[Capability]int{}}, - priceInfo: make(map[string]*big.Rat), - StorageConfigs: make(map[string]*transcodeConfig), - storageMutex: &sync.RWMutex{}, + Eth: e, + WorkDir: wd, + Database: dbh, + AutoAdjustPrice: true, + SegmentChans: make(map[ManifestID]SegmentChan), + segmentMutex: &sync.RWMutex{}, + Capabilities: &Capabilities{capacities: map[Capability]int{}}, + priceInfo: make(map[string]*big.Rat), + priceInfoForCaps: make(map[string]CapabilityPrices), + StorageConfigs: make(map[string]*transcodeConfig), + storageMutex: &sync.RWMutex{}, }, nil } @@ -155,6 +200,33 @@ func (n *LivepeerNode) GetBasePrices() map[string]*big.Rat { return n.priceInfo } +func (n *LivepeerNode) SetBasePriceForCap(b_eth_addr string, cap Capability, modelID string, price *big.Rat) { + addr := strings.ToLower(b_eth_addr) + n.mu.Lock() + defer n.mu.Unlock() + + prices, ok := n.priceInfoForCaps[addr] + if !ok { + prices = NewCapabilityPrices() + n.priceInfoForCaps[addr] = prices + } + + prices.SetPriceForModelID(cap, modelID, price) +} + +func (n *LivepeerNode) GetBasePriceForCap(b_eth_addr string, cap Capability, modelID string) *big.Rat { + addr := strings.ToLower(b_eth_addr) + n.mu.RLock() + defer n.mu.RUnlock() + + prices, ok := n.priceInfoForCaps[addr] + if !ok { + return nil + } + + return prices.PriceForModelID(cap, modelID) +} + // SetMaxFaceValue sets the faceValue upper limit for tickets received func (n *LivepeerNode) SetMaxFaceValue(maxfacevalue *big.Int) { n.mu.Lock() diff --git a/core/orchestrator.go b/core/orchestrator.go index ddecab843..c810fb5b5 100644 --- a/core/orchestrator.go +++ b/core/orchestrator.go @@ -279,7 +279,7 @@ func (orch *orchestrator) PriceInfo(sender ethcommon.Address, manifestID Manifes return nil, nil } - price, err := orch.priceInfo(sender, manifestID) + price, err := orch.priceInfo(sender, manifestID, nil) if err != nil { return nil, err } @@ -294,10 +294,24 @@ func (orch *orchestrator) PriceInfo(sender ethcommon.Address, manifestID Manifes }, nil } -// priceInfo returns price per pixel as a fixed point number wrapped in a big.Rat -func (orch *orchestrator) priceInfo(sender ethcommon.Address, manifestID ManifestID) (*big.Rat, error) { - basePrice := orch.node.GetBasePrice(sender.String()) +func (orch *orchestrator) PriceInfoForCaps(sender ethcommon.Address, manifestID ManifestID, caps *net.Capabilities) (*net.PriceInfo, error) { + if orch.node == nil || orch.node.Recipient == nil { + return nil, nil + } + + price, err := orch.priceInfo(sender, manifestID, caps) + if err != nil { + return nil, err + } + return &net.PriceInfo{ + PricePerUnit: price.Num().Int64(), + PixelsPerUnit: price.Denom().Int64(), + }, nil +} + +// priceInfo returns price per pixel as a fixed point number wrapped in a big.Rat +func (orch *orchestrator) priceInfo(sender ethcommon.Address, manifestID ManifestID, caps *net.Capabilities) (*big.Rat, error) { // If there is already a fixed price for the given session, use this price if manifestID != "" { if balances, ok := orch.node.Balances.balances[sender]; ok { @@ -308,8 +322,33 @@ func (orch *orchestrator) priceInfo(sender ethcommon.Address, manifestID Manifes } } - if basePrice == nil { - basePrice = orch.node.GetBasePrice("default") + var basePrice *big.Rat + if caps == nil { + basePrice = orch.node.GetBasePrice(sender.String()) + if basePrice == nil { + basePrice = orch.node.GetBasePrice("default") + } + } else { + // The base price is the sum of the prices of individual capability + model ID pairs + basePrice = big.NewRat(0, 1) + for cap := range caps.Capacities { + // If the capability does not have constraints (and thus any model constraints) skip it + // because we only price a capability together with a model ID right now + constraints, ok := caps.Constraints[cap] + if !ok { + continue + } + for modelID := range constraints.Models { + price := orch.node.GetBasePriceForCap(sender.String(), Capability(cap), modelID) + if price == nil { + price = orch.node.GetBasePriceForCap("default", Capability(cap), modelID) + } + + if price != nil { + basePrice.Add(basePrice, price) + } + } + } } if !orch.node.AutoAdjustPrice { diff --git a/discovery/db_discovery.go b/discovery/db_discovery.go index f75a9cdb0..caf210666 100644 --- a/discovery/db_discovery.go +++ b/discovery/db_discovery.go @@ -281,7 +281,7 @@ func (dbo *DBOrchestratorPoolCache) cacheDBOrchs() error { return } - info, err := serverGetOrchInfo(ctx, dbo.bcast, uri) + info, err := serverGetOrchInfo(ctx, dbo.bcast, uri, nil) if err != nil { errc <- err return diff --git a/discovery/discovery.go b/discovery/discovery.go index 4b37b9c93..0ffa13278 100644 --- a/discovery/discovery.go +++ b/discovery/discovery.go @@ -106,7 +106,7 @@ func (o *orchestratorPool) GetOrchestrators(ctx context.Context, numOrchestrator return caps.CompatibleWith(info.Capabilities) } getOrchInfo := func(ctx context.Context, od common.OrchestratorDescriptor, infoCh chan common.OrchestratorDescriptor, errCh chan error) { - info, err := serverGetOrchInfo(ctx, o.bcast, od.LocalInfo.URL) + info, err := serverGetOrchInfo(ctx, o.bcast, od.LocalInfo.URL, caps.ToNetCapabilities()) if err == nil && !isBlacklisted(info) && isCompatible(info) { od.RemoteInfo = info infoCh <- od diff --git a/net/lp_rpc.pb.go b/net/lp_rpc.pb.go index 58d8af473..0ccebb681 100644 --- a/net/lp_rpc.pb.go +++ b/net/lp_rpc.pb.go @@ -418,6 +418,8 @@ type OrchestratorRequest struct { Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` // Broadcaster's signature over its address Sig []byte `protobuf:"bytes,2,opt,name=sig,proto3" json:"sig,omitempty"` + // Features and constraints required by the broadcaster + Capabilities *Capabilities `protobuf:"bytes,3,opt,name=capabilities,proto3" json:"capabilities,omitempty"` } func (x *OrchestratorRequest) Reset() { @@ -466,6 +468,13 @@ func (x *OrchestratorRequest) GetSig() []byte { return nil } +func (x *OrchestratorRequest) GetCapabilities() *Capabilities { + if x != nil { + return x.Capabilities + } + return nil +} + // OSInfo needed to negotiate storages that will be used. // It carries info needed to write to the storage. type OSInfo struct { @@ -2100,297 +2109,300 @@ var file_net_lp_rpc_proto_rawDesc = []byte{ 0x6e, 0x65, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x1f, 0x0a, 0x1d, 0x45, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x41, 0x0a, 0x13, 0x4f, 0x72, 0x63, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x78, 0x0a, 0x13, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, - 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x22, 0x99, 0x01, 0x0a, - 0x06, 0x4f, 0x53, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x39, 0x0a, 0x0b, 0x73, 0x74, 0x6f, 0x72, 0x61, - 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6e, - 0x65, 0x74, 0x2e, 0x4f, 0x53, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, - 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x25, 0x0a, 0x06, 0x73, 0x33, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x10, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x33, 0x4f, 0x53, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x06, 0x73, 0x33, 0x69, 0x6e, 0x66, 0x6f, 0x22, 0x2d, 0x0a, 0x0b, 0x53, 0x74, 0x6f, - 0x72, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x49, 0x52, 0x45, - 0x43, 0x54, 0x10, 0x00, 0x12, 0x06, 0x0a, 0x02, 0x53, 0x33, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, - 0x47, 0x4f, 0x4f, 0x47, 0x4c, 0x45, 0x10, 0x02, 0x22, 0xa2, 0x01, 0x0a, 0x08, 0x53, 0x33, 0x4f, - 0x53, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, - 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x78, 0x41, 0x6d, 0x7a, 0x44, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x78, 0x41, 0x6d, 0x7a, 0x44, 0x61, 0x74, 0x65, 0x22, 0x55, 0x0a, - 0x09, 0x50, 0x72, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x72, - 0x69, 0x63, 0x65, 0x50, 0x65, 0x72, 0x55, 0x6e, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x0c, 0x70, 0x72, 0x69, 0x63, 0x65, 0x50, 0x65, 0x72, 0x55, 0x6e, 0x69, 0x74, 0x12, 0x24, - 0x0a, 0x0d, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x50, 0x65, 0x72, 0x55, 0x6e, 0x69, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x50, 0x65, 0x72, - 0x55, 0x6e, 0x69, 0x74, 0x22, 0xd9, 0x04, 0x0a, 0x0c, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, - 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x62, 0x69, 0x74, 0x73, 0x74, 0x72, 0x69, - 0x6e, 0x67, 0x18, 0x01, 0x20, 0x03, 0x28, 0x04, 0x52, 0x09, 0x62, 0x69, 0x74, 0x73, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x12, 0x20, 0x0a, 0x0b, 0x6d, 0x61, 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x69, - 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x6e, 0x64, 0x61, 0x74, - 0x6f, 0x72, 0x69, 0x65, 0x73, 0x12, 0x41, 0x0a, 0x0a, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, - 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6e, 0x65, 0x74, 0x2e, - 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x43, 0x61, 0x70, - 0x61, 0x63, 0x69, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x63, 0x61, - 0x70, 0x61, 0x63, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x44, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x73, - 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, - 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, - 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x1a, 0x3d, - 0x0a, 0x0f, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, - 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0xe1, 0x01, - 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x41, 0x0a, - 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, + 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x12, 0x35, 0x0a, 0x0c, + 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, + 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, + 0x69, 0x65, 0x73, 0x22, 0x99, 0x01, 0x0a, 0x06, 0x4f, 0x53, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x39, + 0x0a, 0x0b, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4f, 0x53, 0x49, 0x6e, 0x66, 0x6f, + 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x73, 0x74, + 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x25, 0x0a, 0x06, 0x73, 0x33, 0x69, + 0x6e, 0x66, 0x6f, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x6e, 0x65, 0x74, 0x2e, + 0x53, 0x33, 0x4f, 0x53, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x73, 0x33, 0x69, 0x6e, 0x66, 0x6f, + 0x22, 0x2d, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x0a, 0x0a, 0x06, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x10, 0x00, 0x12, 0x06, 0x0a, 0x02, 0x53, + 0x33, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x47, 0x4f, 0x4f, 0x47, 0x4c, 0x45, 0x10, 0x02, 0x22, + 0xa2, 0x01, 0x0a, 0x08, 0x53, 0x33, 0x4f, 0x53, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, + 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, + 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x72, + 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x78, 0x41, 0x6d, 0x7a, + 0x44, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x78, 0x41, 0x6d, 0x7a, + 0x44, 0x61, 0x74, 0x65, 0x22, 0x55, 0x0a, 0x09, 0x50, 0x72, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x72, 0x69, 0x63, 0x65, 0x50, 0x65, 0x72, 0x55, 0x6e, 0x69, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x70, 0x72, 0x69, 0x63, 0x65, 0x50, 0x65, + 0x72, 0x55, 0x6e, 0x69, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x50, + 0x65, 0x72, 0x55, 0x6e, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x70, 0x69, + 0x78, 0x65, 0x6c, 0x73, 0x50, 0x65, 0x72, 0x55, 0x6e, 0x69, 0x74, 0x22, 0xd9, 0x04, 0x0a, 0x0c, + 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, + 0x62, 0x69, 0x74, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x03, 0x28, 0x04, 0x52, + 0x09, 0x62, 0x69, 0x74, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x20, 0x0a, 0x0b, 0x6d, 0x61, + 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, + 0x0b, 0x6d, 0x61, 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x12, 0x41, 0x0a, 0x0a, + 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x21, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, + 0x69, 0x65, 0x73, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x0a, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, + 0x44, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, + 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, + 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, + 0x61, 0x69, 0x6e, 0x74, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, + 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x1a, 0xe1, 0x01, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, + 0x69, 0x6e, 0x74, 0x73, 0x12, 0x41, 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, + 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, + 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x1a, 0x25, 0x0a, 0x0f, 0x4d, 0x6f, 0x64, 0x65, 0x6c, + 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x77, 0x61, + 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x77, 0x61, 0x72, 0x6d, 0x1a, 0x68, + 0x0a, 0x0b, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x43, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, + 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, + 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x6f, + 0x64, 0x65, 0x6c, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5d, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x73, + 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x33, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, - 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x6f, 0x64, - 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, - 0x1a, 0x25, 0x0a, 0x0f, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, - 0x69, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x77, 0x61, 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x04, 0x77, 0x61, 0x72, 0x6d, 0x1a, 0x68, 0x0a, 0x0b, 0x4d, 0x6f, 0x64, 0x65, 0x6c, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x43, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, - 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x74, - 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x43, 0x6f, 0x6e, 0x73, - 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x1a, 0x5d, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x33, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, - 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, - 0x61, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x22, 0xc0, 0x02, 0x0a, 0x10, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, - 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, - 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x72, 0x61, 0x6e, 0x73, - 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x36, 0x0a, 0x0d, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x5f, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, - 0x65, 0x74, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, - 0x0c, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x2d, 0x0a, - 0x0a, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x0e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x72, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x09, 0x70, 0x72, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x0a, 0x07, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x35, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, - 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, - 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, - 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x2d, 0x0a, - 0x0a, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x0e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, - 0x6e, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x25, 0x0a, 0x07, - 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x20, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0b, 0x2e, - 0x6e, 0x65, 0x74, 0x2e, 0x4f, 0x53, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x73, 0x74, 0x6f, 0x72, - 0x61, 0x67, 0x65, 0x22, 0x60, 0x0a, 0x09, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, - 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xf4, 0x04, 0x0a, 0x07, 0x53, 0x65, 0x67, 0x44, 0x61, 0x74, - 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x49, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x49, - 0x64, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x65, 0x71, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, - 0x73, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, - 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, - 0x6c, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x03, 0x73, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x35, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, - 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, - 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61, - 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x68, - 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6e, - 0x65, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x09, 0x61, 0x75, - 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x30, 0x0a, 0x14, 0x63, 0x61, 0x6c, 0x63, 0x5f, - 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, - 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x63, 0x61, 0x6c, 0x63, 0x50, 0x65, 0x72, 0x63, 0x65, - 0x70, 0x74, 0x75, 0x61, 0x6c, 0x48, 0x61, 0x73, 0x68, 0x12, 0x25, 0x0a, 0x07, 0x73, 0x74, 0x6f, - 0x72, 0x61, 0x67, 0x65, 0x18, 0x20, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6e, 0x65, 0x74, - 0x2e, 0x4f, 0x53, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, - 0x12, 0x35, 0x0a, 0x0c, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, - 0x18, 0x21, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, - 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x0c, 0x66, 0x75, 0x6c, 0x6c, 0x50, - 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x0d, 0x66, 0x75, 0x6c, 0x6c, 0x50, - 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x32, 0x18, 0x22, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, + 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc0, 0x02, 0x0a, 0x10, 0x4f, 0x72, 0x63, 0x68, + 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1e, 0x0a, 0x0a, + 0x74, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x36, 0x0a, 0x0d, + 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x0c, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x12, 0x2d, 0x0a, 0x0a, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x6e, + 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, + 0x72, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x70, 0x72, 0x69, 0x63, 0x65, 0x49, + 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x35, 0x0a, + 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, + 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, + 0x74, 0x69, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, + 0x65, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x41, + 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x12, 0x25, 0x0a, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x20, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4f, 0x53, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x22, 0x60, 0x0a, 0x09, 0x41, 0x75, + 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1d, 0x0a, + 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, + 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xf4, 0x04, 0x0a, + 0x07, 0x53, 0x65, 0x67, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x6e, 0x69, + 0x66, 0x65, 0x73, 0x74, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6d, 0x61, + 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x65, 0x71, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x73, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, + 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1a, + 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, + 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x35, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61, + 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, + 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, + 0x73, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, + 0x2d, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x30, + 0x0a, 0x14, 0x63, 0x61, 0x6c, 0x63, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, + 0x6c, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x63, 0x61, + 0x6c, 0x63, 0x50, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x48, 0x61, 0x73, 0x68, + 0x12, 0x25, 0x0a, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x20, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x0b, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4f, 0x53, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, + 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x35, 0x0a, 0x0c, 0x66, 0x75, 0x6c, 0x6c, 0x50, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x21, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, + 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x52, 0x0c, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x37, + 0x0a, 0x0d, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x32, 0x18, + 0x22, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, 0x65, + 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x0d, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x72, + 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x32, 0x12, 0x37, 0x0a, 0x0d, 0x66, 0x75, 0x6c, 0x6c, 0x50, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x33, 0x18, 0x23, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, - 0x65, 0x52, 0x0d, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x32, - 0x12, 0x37, 0x0a, 0x0d, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, - 0x33, 0x18, 0x23, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, - 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x0d, 0x66, 0x75, 0x6c, 0x6c, - 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x33, 0x12, 0x41, 0x0a, 0x12, 0x73, 0x65, 0x67, - 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, - 0x25, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x65, 0x67, 0x50, - 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x52, 0x11, 0x73, 0x65, 0x67, 0x6d, 0x65, - 0x6e, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x2e, 0x0a, 0x12, - 0x46, 0x6f, 0x72, 0x63, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x69, 0x6e, - 0x69, 0x74, 0x18, 0x26, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x53, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x69, 0x6e, 0x69, 0x74, 0x22, 0x33, 0x0a, 0x0d, - 0x53, 0x65, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, - 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x66, 0x72, 0x6f, - 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x74, - 0x6f, 0x22, 0xcc, 0x05, 0x0a, 0x0c, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, - 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, - 0x11, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, - 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x12, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x68, 0x65, - 0x69, 0x67, 0x68, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x72, 0x61, 0x74, 0x65, 0x18, - 0x13, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x62, 0x69, 0x74, 0x72, 0x61, 0x74, 0x65, 0x12, 0x10, - 0x0a, 0x03, 0x66, 0x70, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x66, 0x70, 0x73, - 0x12, 0x30, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x18, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, - 0x69, 0x6c, 0x65, 0x2e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, - 0x61, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x70, 0x73, 0x44, 0x65, 0x6e, 0x18, 0x16, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x06, 0x66, 0x70, 0x73, 0x44, 0x65, 0x6e, 0x12, 0x33, 0x0a, 0x07, 0x70, 0x72, - 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x6e, 0x65, - 0x74, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x50, - 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, - 0x10, 0x0a, 0x03, 0x67, 0x6f, 0x70, 0x18, 0x18, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x67, 0x6f, - 0x70, 0x12, 0x36, 0x0a, 0x07, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x18, 0x19, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, - 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, - 0x52, 0x07, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, - 0x6f, 0x72, 0x44, 0x65, 0x70, 0x74, 0x68, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x63, - 0x6f, 0x6c, 0x6f, 0x72, 0x44, 0x65, 0x70, 0x74, 0x68, 0x12, 0x47, 0x0a, 0x0c, 0x63, 0x68, 0x72, - 0x6f, 0x6d, 0x61, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x23, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, - 0x6c, 0x65, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x53, 0x75, 0x62, 0x73, 0x61, 0x6d, 0x70, - 0x6c, 0x69, 0x6e, 0x67, 0x52, 0x0c, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x46, 0x6f, 0x72, 0x6d, - 0x61, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x1c, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x07, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x22, 0x1d, 0x0a, 0x06, - 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x50, 0x45, 0x47, 0x54, 0x53, - 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4d, 0x50, 0x34, 0x10, 0x01, 0x22, 0x6a, 0x0a, 0x07, 0x50, - 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x13, 0x0a, 0x0f, 0x45, 0x4e, 0x43, 0x4f, 0x44, 0x45, - 0x52, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x48, - 0x32, 0x36, 0x34, 0x5f, 0x42, 0x41, 0x53, 0x45, 0x4c, 0x49, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x0d, - 0x0a, 0x09, 0x48, 0x32, 0x36, 0x34, 0x5f, 0x4d, 0x41, 0x49, 0x4e, 0x10, 0x02, 0x12, 0x0d, 0x0a, - 0x09, 0x48, 0x32, 0x36, 0x34, 0x5f, 0x48, 0x49, 0x47, 0x48, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, - 0x48, 0x32, 0x36, 0x34, 0x5f, 0x43, 0x4f, 0x4e, 0x53, 0x54, 0x52, 0x41, 0x49, 0x4e, 0x45, 0x44, - 0x5f, 0x48, 0x49, 0x47, 0x48, 0x10, 0x04, 0x22, 0x32, 0x0a, 0x0a, 0x56, 0x69, 0x64, 0x65, 0x6f, - 0x43, 0x6f, 0x64, 0x65, 0x63, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x32, 0x36, 0x34, 0x10, 0x00, 0x12, - 0x08, 0x0a, 0x04, 0x48, 0x32, 0x36, 0x35, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x56, 0x50, 0x38, - 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x56, 0x50, 0x39, 0x10, 0x03, 0x22, 0x43, 0x0a, 0x11, 0x43, - 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x53, 0x75, 0x62, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, - 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x48, 0x52, 0x4f, 0x4d, 0x41, 0x5f, 0x34, 0x32, 0x30, 0x10, 0x00, - 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x48, 0x52, 0x4f, 0x4d, 0x41, 0x5f, 0x34, 0x32, 0x32, 0x10, 0x01, - 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x48, 0x52, 0x4f, 0x4d, 0x41, 0x5f, 0x34, 0x34, 0x34, 0x10, 0x02, - 0x22, 0x71, 0x0a, 0x15, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x53, 0x65, - 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x70, - 0x69, 0x78, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x70, 0x69, 0x78, - 0x65, 0x6c, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, - 0x6c, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x11, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x48, 0x61, 0x73, 0x68, - 0x55, 0x72, 0x6c, 0x22, 0x59, 0x0a, 0x0d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, - 0x44, 0x61, 0x74, 0x61, 0x12, 0x36, 0x0a, 0x08, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x72, 0x61, - 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x61, - 0x74, 0x61, 0x52, 0x08, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, - 0x73, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x22, 0x9a, - 0x01, 0x0a, 0x0f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x65, 0x71, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x03, 0x73, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x28, 0x0a, 0x04, - 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6e, 0x65, 0x74, - 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, - 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x29, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x10, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4f, 0x72, 0x63, 0x68, 0x65, - 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e, 0x66, - 0x6f, 0x42, 0x08, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x7c, 0x0a, 0x0f, 0x52, - 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, - 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, - 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, - 0x74, 0x79, 0x12, 0x35, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, - 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, - 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x0c, 0x63, 0x61, 0x70, - 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x22, 0x89, 0x01, 0x0a, 0x0d, 0x4e, 0x6f, - 0x74, 0x69, 0x66, 0x79, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, - 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x26, 0x0a, - 0x07, 0x73, 0x65, 0x67, 0x44, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, - 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x65, 0x67, 0x44, 0x61, 0x74, 0x61, 0x52, 0x07, 0x73, 0x65, - 0x67, 0x44, 0x61, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x18, - 0x10, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x12, 0x1a, 0x0a, - 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, - 0x04, 0x08, 0x21, 0x10, 0x22, 0x22, 0x9f, 0x02, 0x0a, 0x0c, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, - 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, - 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, - 0x69, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x66, 0x61, 0x63, 0x65, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x77, 0x69, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x62, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x77, 0x69, 0x6e, 0x50, 0x72, 0x6f, 0x62, 0x12, 0x2e, - 0x0a, 0x13, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x64, - 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x72, 0x65, 0x63, - 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x6e, 0x64, 0x48, 0x61, 0x73, 0x68, 0x12, 0x12, - 0x0a, 0x04, 0x73, 0x65, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x73, 0x65, - 0x65, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x65, 0x78, - 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x48, 0x0a, - 0x11, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x72, 0x61, - 0x6d, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, - 0x69, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, - 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x10, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x49, 0x0a, 0x12, 0x54, 0x69, 0x63, 0x6b, 0x65, - 0x74, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x21, 0x0a, - 0x0c, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x4e, 0x6f, 0x6e, 0x63, 0x65, - 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, - 0x69, 0x67, 0x22, 0x7a, 0x0a, 0x16, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x70, 0x69, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x25, 0x0a, 0x0e, - 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x6f, - 0x75, 0x6e, 0x64, 0x12, 0x39, 0x0a, 0x19, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x16, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x22, 0xa5, - 0x02, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x36, 0x0a, 0x0d, 0x74, 0x69, - 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x73, 0x52, 0x0c, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, - 0x6d, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x48, 0x0a, 0x11, 0x65, 0x78, - 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x69, 0x63, 0x6b, - 0x65, 0x74, 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, - 0x6d, 0x73, 0x52, 0x10, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x73, 0x12, 0x49, 0x0a, 0x14, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x73, - 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x53, - 0x65, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x12, 0x74, 0x69, 0x63, - 0x6b, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, - 0x35, 0x0a, 0x0e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x63, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x72, - 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, - 0x64, 0x50, 0x72, 0x69, 0x63, 0x65, 0x32, 0xd8, 0x01, 0x0a, 0x0c, 0x4f, 0x72, 0x63, 0x68, 0x65, - 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x42, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x4f, 0x72, - 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x18, 0x2e, 0x6e, 0x65, 0x74, - 0x2e, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4f, 0x72, 0x63, 0x68, 0x65, - 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x5e, 0x0a, 0x15, 0x45, - 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x54, 0x72, - 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, - 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x50, - 0x69, 0x6e, 0x67, 0x12, 0x0d, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x50, 0x6f, - 0x6e, 0x67, 0x1a, 0x0d, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6e, - 0x67, 0x32, 0x4e, 0x0a, 0x0a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, - 0x40, 0x0a, 0x12, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x54, 0x72, 0x61, 0x6e, 0x73, - 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x14, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x52, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6e, 0x65, - 0x74, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x30, - 0x01, 0x42, 0x07, 0x5a, 0x05, 0x2e, 0x2f, 0x6e, 0x65, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x65, 0x52, 0x0d, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x33, + 0x12, 0x41, 0x0a, 0x12, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x25, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6e, + 0x65, 0x74, 0x2e, 0x53, 0x65, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, + 0x52, 0x11, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, + 0x65, 0x72, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x26, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x12, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x69, + 0x6e, 0x69, 0x74, 0x22, 0x33, 0x0a, 0x0d, 0x53, 0x65, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, + 0x74, 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x74, 0x6f, 0x22, 0xcc, 0x05, 0x0a, 0x0c, 0x56, 0x69, 0x64, + 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x11, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x12, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x62, + 0x69, 0x74, 0x72, 0x61, 0x74, 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x62, 0x69, + 0x74, 0x72, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x70, 0x73, 0x18, 0x14, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x03, 0x66, 0x70, 0x73, 0x12, 0x30, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, + 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x46, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x70, 0x73, + 0x44, 0x65, 0x6e, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x66, 0x70, 0x73, 0x44, 0x65, + 0x6e, 0x12, 0x33, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x17, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, + 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x07, 0x70, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x6f, 0x70, 0x18, 0x18, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x03, 0x67, 0x6f, 0x70, 0x12, 0x36, 0x0a, 0x07, 0x65, 0x6e, 0x63, 0x6f, + 0x64, 0x65, 0x72, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x6e, 0x65, 0x74, 0x2e, + 0x56, 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x56, 0x69, 0x64, + 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x52, 0x07, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, + 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x44, 0x65, 0x70, 0x74, 0x68, 0x18, 0x1a, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x44, 0x65, 0x70, 0x74, 0x68, + 0x12, 0x47, 0x0a, 0x0c, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x18, 0x1b, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, + 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x61, + 0x53, 0x75, 0x62, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x52, 0x0c, 0x63, 0x68, 0x72, + 0x6f, 0x6d, 0x61, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x71, 0x75, 0x61, + 0x6c, 0x69, 0x74, 0x79, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x71, 0x75, 0x61, 0x6c, + 0x69, 0x74, 0x79, 0x22, 0x1d, 0x0a, 0x06, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x0a, 0x0a, + 0x06, 0x4d, 0x50, 0x45, 0x47, 0x54, 0x53, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4d, 0x50, 0x34, + 0x10, 0x01, 0x22, 0x6a, 0x0a, 0x07, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x13, 0x0a, + 0x0f, 0x45, 0x4e, 0x43, 0x4f, 0x44, 0x45, 0x52, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, + 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x48, 0x32, 0x36, 0x34, 0x5f, 0x42, 0x41, 0x53, 0x45, 0x4c, + 0x49, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x48, 0x32, 0x36, 0x34, 0x5f, 0x4d, 0x41, + 0x49, 0x4e, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x48, 0x32, 0x36, 0x34, 0x5f, 0x48, 0x49, 0x47, + 0x48, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x48, 0x32, 0x36, 0x34, 0x5f, 0x43, 0x4f, 0x4e, 0x53, + 0x54, 0x52, 0x41, 0x49, 0x4e, 0x45, 0x44, 0x5f, 0x48, 0x49, 0x47, 0x48, 0x10, 0x04, 0x22, 0x32, + 0x0a, 0x0a, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x12, 0x08, 0x0a, 0x04, + 0x48, 0x32, 0x36, 0x34, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x32, 0x36, 0x35, 0x10, 0x01, + 0x12, 0x07, 0x0a, 0x03, 0x56, 0x50, 0x38, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x56, 0x50, 0x39, + 0x10, 0x03, 0x22, 0x43, 0x0a, 0x11, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x53, 0x75, 0x62, 0x73, + 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x48, 0x52, 0x4f, 0x4d, + 0x41, 0x5f, 0x34, 0x32, 0x30, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x48, 0x52, 0x4f, 0x4d, + 0x41, 0x5f, 0x34, 0x32, 0x32, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x48, 0x52, 0x4f, 0x4d, + 0x41, 0x5f, 0x34, 0x34, 0x34, 0x10, 0x02, 0x22, 0x71, 0x0a, 0x15, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x63, 0x6f, 0x64, 0x65, 0x64, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, + 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, + 0x72, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x06, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x70, 0x65, + 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x75, 0x72, + 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, + 0x75, 0x61, 0x6c, 0x48, 0x61, 0x73, 0x68, 0x55, 0x72, 0x6c, 0x22, 0x59, 0x0a, 0x0d, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x36, 0x0a, 0x08, 0x73, + 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x53, 0x65, + 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x73, 0x65, 0x67, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x03, 0x73, 0x69, 0x67, 0x22, 0x9a, 0x01, 0x0a, 0x0f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, + 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x65, 0x71, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x73, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x05, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x12, 0x28, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x12, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, + 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x29, 0x0a, + 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6e, 0x65, + 0x74, 0x2e, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x42, 0x08, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x22, 0x7c, 0x0a, 0x0f, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x35, 0x0a, 0x0c, 0x63, 0x61, 0x70, + 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, + 0x65, 0x73, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, + 0x22, 0x89, 0x01, 0x0a, 0x0d, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, 0x65, 0x67, 0x6d, 0x65, + 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x75, 0x72, 0x6c, 0x12, 0x26, 0x0a, 0x07, 0x73, 0x65, 0x67, 0x44, 0x61, 0x74, 0x61, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x65, 0x67, 0x44, + 0x61, 0x74, 0x61, 0x52, 0x07, 0x73, 0x65, 0x67, 0x44, 0x61, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x06, + 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x18, 0x10, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x74, 0x61, + 0x73, 0x6b, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, + 0x18, 0x11, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, + 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x21, 0x10, 0x22, 0x22, 0x9f, 0x02, 0x0a, + 0x0c, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x1c, 0x0a, + 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x66, + 0x61, 0x63, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x09, 0x66, 0x61, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x77, 0x69, + 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x62, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x77, 0x69, + 0x6e, 0x50, 0x72, 0x6f, 0x62, 0x12, 0x2e, 0x0a, 0x13, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, + 0x6e, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x64, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x11, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x6e, + 0x64, 0x48, 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x65, 0x64, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x04, 0x73, 0x65, 0x65, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x78, 0x70, + 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x48, 0x0a, 0x11, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1b, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x70, 0x69, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x10, 0x65, 0x78, + 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x49, + 0x0a, 0x12, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x6e, + 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x73, 0x65, 0x6e, 0x64, + 0x65, 0x72, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x22, 0x7a, 0x0a, 0x16, 0x54, 0x69, 0x63, + 0x6b, 0x65, 0x74, 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x39, 0x0a, 0x19, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x16, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x48, 0x61, 0x73, 0x68, 0x22, 0xa5, 0x02, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x12, 0x36, 0x0a, 0x0d, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, + 0x69, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x0c, 0x74, 0x69, 0x63, + 0x6b, 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, + 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, + 0x72, 0x12, 0x48, 0x0a, 0x11, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6e, + 0x65, 0x74, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x10, 0x65, 0x78, 0x70, 0x69, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x49, 0x0a, 0x14, 0x74, + 0x69, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6e, 0x65, 0x74, 0x2e, + 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x73, 0x52, 0x12, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x35, 0x0a, 0x0e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, + 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x72, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, + 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x72, 0x69, 0x63, 0x65, 0x32, 0xd8, 0x01, + 0x0a, 0x0c, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x42, + 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x12, 0x18, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x6e, 0x65, + 0x74, 0x2e, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, + 0x66, 0x6f, 0x12, 0x5e, 0x0a, 0x15, 0x45, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, + 0x64, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x2e, 0x6e, 0x65, + 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, + 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, + 0x69, 0x6e, 0x67, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x0d, 0x2e, 0x6e, 0x65, 0x74, + 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6e, 0x67, 0x1a, 0x0d, 0x2e, 0x6e, 0x65, 0x74, 0x2e, + 0x50, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6e, 0x67, 0x32, 0x4e, 0x0a, 0x0a, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x40, 0x0a, 0x12, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x65, 0x72, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x14, 0x2e, 0x6e, + 0x65, 0x74, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, + 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x42, 0x07, 0x5a, 0x05, 0x2e, 0x2f, 0x6e, 0x65, + 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2443,52 +2455,53 @@ var file_net_lp_rpc_proto_goTypes = []interface{}{ } var file_net_lp_rpc_proto_depIdxs = []int32{ 14, // 0: net.EndTranscodingSessionRequest.auth_token:type_name -> net.AuthToken - 0, // 1: net.OSInfo.storageType:type_name -> net.OSInfo.StorageType - 10, // 2: net.OSInfo.s3info:type_name -> net.S3OSInfo - 27, // 3: net.Capabilities.capacities:type_name -> net.Capabilities.CapacitiesEntry - 29, // 4: net.Capabilities.constraints:type_name -> net.Capabilities.ConstraintsEntry - 23, // 5: net.OrchestratorInfo.ticket_params:type_name -> net.TicketParams - 11, // 6: net.OrchestratorInfo.price_info:type_name -> net.PriceInfo - 12, // 7: net.OrchestratorInfo.capabilities:type_name -> net.Capabilities - 14, // 8: net.OrchestratorInfo.auth_token:type_name -> net.AuthToken - 9, // 9: net.OrchestratorInfo.storage:type_name -> net.OSInfo - 12, // 10: net.SegData.capabilities:type_name -> net.Capabilities - 14, // 11: net.SegData.auth_token:type_name -> net.AuthToken - 9, // 12: net.SegData.storage:type_name -> net.OSInfo - 17, // 13: net.SegData.fullProfiles:type_name -> net.VideoProfile - 17, // 14: net.SegData.fullProfiles2:type_name -> net.VideoProfile - 17, // 15: net.SegData.fullProfiles3:type_name -> net.VideoProfile - 16, // 16: net.SegData.segment_parameters:type_name -> net.SegParameters - 1, // 17: net.VideoProfile.format:type_name -> net.VideoProfile.Format - 2, // 18: net.VideoProfile.profile:type_name -> net.VideoProfile.Profile - 3, // 19: net.VideoProfile.encoder:type_name -> net.VideoProfile.VideoCodec - 4, // 20: net.VideoProfile.chromaFormat:type_name -> net.VideoProfile.ChromaSubsampling - 18, // 21: net.TranscodeData.segments:type_name -> net.TranscodedSegmentData - 19, // 22: net.TranscodeResult.data:type_name -> net.TranscodeData - 13, // 23: net.TranscodeResult.info:type_name -> net.OrchestratorInfo - 12, // 24: net.RegisterRequest.capabilities:type_name -> net.Capabilities - 15, // 25: net.NotifySegment.segData:type_name -> net.SegData - 25, // 26: net.TicketParams.expiration_params:type_name -> net.TicketExpirationParams - 23, // 27: net.Payment.ticket_params:type_name -> net.TicketParams - 25, // 28: net.Payment.expiration_params:type_name -> net.TicketExpirationParams - 24, // 29: net.Payment.ticket_sender_params:type_name -> net.TicketSenderParams - 11, // 30: net.Payment.expected_price:type_name -> net.PriceInfo - 31, // 31: net.Capabilities.Constraints.models:type_name -> net.Capabilities.Constraints.ModelsEntry - 28, // 32: net.Capabilities.ConstraintsEntry.value:type_name -> net.Capabilities.Constraints - 30, // 33: net.Capabilities.Constraints.ModelsEntry.value:type_name -> net.Capabilities.Constraints.ModelConstraint - 8, // 34: net.Orchestrator.GetOrchestrator:input_type -> net.OrchestratorRequest - 6, // 35: net.Orchestrator.EndTranscodingSession:input_type -> net.EndTranscodingSessionRequest - 5, // 36: net.Orchestrator.Ping:input_type -> net.PingPong - 21, // 37: net.Transcoder.RegisterTranscoder:input_type -> net.RegisterRequest - 13, // 38: net.Orchestrator.GetOrchestrator:output_type -> net.OrchestratorInfo - 7, // 39: net.Orchestrator.EndTranscodingSession:output_type -> net.EndTranscodingSessionResponse - 5, // 40: net.Orchestrator.Ping:output_type -> net.PingPong - 22, // 41: net.Transcoder.RegisterTranscoder:output_type -> net.NotifySegment - 38, // [38:42] is the sub-list for method output_type - 34, // [34:38] is the sub-list for method input_type - 34, // [34:34] is the sub-list for extension type_name - 34, // [34:34] is the sub-list for extension extendee - 0, // [0:34] is the sub-list for field type_name + 12, // 1: net.OrchestratorRequest.capabilities:type_name -> net.Capabilities + 0, // 2: net.OSInfo.storageType:type_name -> net.OSInfo.StorageType + 10, // 3: net.OSInfo.s3info:type_name -> net.S3OSInfo + 27, // 4: net.Capabilities.capacities:type_name -> net.Capabilities.CapacitiesEntry + 29, // 5: net.Capabilities.constraints:type_name -> net.Capabilities.ConstraintsEntry + 23, // 6: net.OrchestratorInfo.ticket_params:type_name -> net.TicketParams + 11, // 7: net.OrchestratorInfo.price_info:type_name -> net.PriceInfo + 12, // 8: net.OrchestratorInfo.capabilities:type_name -> net.Capabilities + 14, // 9: net.OrchestratorInfo.auth_token:type_name -> net.AuthToken + 9, // 10: net.OrchestratorInfo.storage:type_name -> net.OSInfo + 12, // 11: net.SegData.capabilities:type_name -> net.Capabilities + 14, // 12: net.SegData.auth_token:type_name -> net.AuthToken + 9, // 13: net.SegData.storage:type_name -> net.OSInfo + 17, // 14: net.SegData.fullProfiles:type_name -> net.VideoProfile + 17, // 15: net.SegData.fullProfiles2:type_name -> net.VideoProfile + 17, // 16: net.SegData.fullProfiles3:type_name -> net.VideoProfile + 16, // 17: net.SegData.segment_parameters:type_name -> net.SegParameters + 1, // 18: net.VideoProfile.format:type_name -> net.VideoProfile.Format + 2, // 19: net.VideoProfile.profile:type_name -> net.VideoProfile.Profile + 3, // 20: net.VideoProfile.encoder:type_name -> net.VideoProfile.VideoCodec + 4, // 21: net.VideoProfile.chromaFormat:type_name -> net.VideoProfile.ChromaSubsampling + 18, // 22: net.TranscodeData.segments:type_name -> net.TranscodedSegmentData + 19, // 23: net.TranscodeResult.data:type_name -> net.TranscodeData + 13, // 24: net.TranscodeResult.info:type_name -> net.OrchestratorInfo + 12, // 25: net.RegisterRequest.capabilities:type_name -> net.Capabilities + 15, // 26: net.NotifySegment.segData:type_name -> net.SegData + 25, // 27: net.TicketParams.expiration_params:type_name -> net.TicketExpirationParams + 23, // 28: net.Payment.ticket_params:type_name -> net.TicketParams + 25, // 29: net.Payment.expiration_params:type_name -> net.TicketExpirationParams + 24, // 30: net.Payment.ticket_sender_params:type_name -> net.TicketSenderParams + 11, // 31: net.Payment.expected_price:type_name -> net.PriceInfo + 31, // 32: net.Capabilities.Constraints.models:type_name -> net.Capabilities.Constraints.ModelsEntry + 28, // 33: net.Capabilities.ConstraintsEntry.value:type_name -> net.Capabilities.Constraints + 30, // 34: net.Capabilities.Constraints.ModelsEntry.value:type_name -> net.Capabilities.Constraints.ModelConstraint + 8, // 35: net.Orchestrator.GetOrchestrator:input_type -> net.OrchestratorRequest + 6, // 36: net.Orchestrator.EndTranscodingSession:input_type -> net.EndTranscodingSessionRequest + 5, // 37: net.Orchestrator.Ping:input_type -> net.PingPong + 21, // 38: net.Transcoder.RegisterTranscoder:input_type -> net.RegisterRequest + 13, // 39: net.Orchestrator.GetOrchestrator:output_type -> net.OrchestratorInfo + 7, // 40: net.Orchestrator.EndTranscodingSession:output_type -> net.EndTranscodingSessionResponse + 5, // 41: net.Orchestrator.Ping:output_type -> net.PingPong + 22, // 42: net.Transcoder.RegisterTranscoder:output_type -> net.NotifySegment + 39, // [39:43] is the sub-list for method output_type + 35, // [35:39] is the sub-list for method input_type + 35, // [35:35] is the sub-list for extension type_name + 35, // [35:35] is the sub-list for extension extendee + 0, // [0:35] is the sub-list for field type_name } func init() { file_net_lp_rpc_proto_init() } diff --git a/net/lp_rpc.proto b/net/lp_rpc.proto index 35d6e1920..0eddc9fee 100644 --- a/net/lp_rpc.proto +++ b/net/lp_rpc.proto @@ -43,6 +43,9 @@ message OrchestratorRequest { // Broadcaster's signature over its address bytes sig = 2; + + // Features and constraints required by the broadcaster + Capabilities capabilities = 3; } /* diff --git a/server/broadcast.go b/server/broadcast.go index 898495d02..f96b02bfb 100755 --- a/server/broadcast.go +++ b/server/broadcast.go @@ -1486,7 +1486,7 @@ func refreshSession(ctx context.Context, sess *BroadcastSession) error { ctx, cancel := context.WithTimeout(ctx, refreshTimeout) defer cancel() - oInfo, err := getOrchestratorInfoRPC(ctx, sess.Broadcaster, uri) + oInfo, err := getOrchestratorInfoRPC(ctx, sess.Broadcaster, uri, sess.Params.Capabilities.ToNetCapabilities()) if err != nil { return err } diff --git a/server/rpc.go b/server/rpc.go index 7f6704460..29b22598b 100644 --- a/server/rpc.go +++ b/server/rpc.go @@ -57,6 +57,7 @@ type Orchestrator interface { ProcessPayment(ctx context.Context, payment net.Payment, manifestID core.ManifestID) error TicketParams(sender ethcommon.Address, priceInfo *net.PriceInfo) (*net.TicketParams, error) PriceInfo(sender ethcommon.Address, manifestID core.ManifestID) (*net.PriceInfo, error) + PriceInfoForCaps(sender ethcommon.Address, manifestID core.ManifestID, caps *net.Capabilities) (*net.PriceInfo, error) SufficientBalance(addr ethcommon.Address, manifestID core.ManifestID) bool DebitFees(addr ethcommon.Address, manifestID core.ManifestID, price *net.PriceInfo, pixels int64) Capabilities() *net.Capabilities @@ -260,14 +261,14 @@ func ping(context context.Context, req *net.PingPong, orch Orchestrator) (*net.P } // GetOrchestratorInfo - the broadcaster calls GetOrchestratorInfo which invokes GetOrchestrator on the orchestrator -func GetOrchestratorInfo(ctx context.Context, bcast common.Broadcaster, orchestratorServer *url.URL) (*net.OrchestratorInfo, error) { +func GetOrchestratorInfo(ctx context.Context, bcast common.Broadcaster, orchestratorServer *url.URL, caps *net.Capabilities) (*net.OrchestratorInfo, error) { c, conn, err := startOrchestratorClient(ctx, orchestratorServer) if err != nil { return nil, err } defer conn.Close() - req, err := genOrchestratorReq(bcast) + req, err := genOrchestratorReq(bcast, caps) r, err := c.GetOrchestrator(ctx, req) if err != nil { return nil, errors.Wrapf(err, "Could not get orchestrator orch=%v", orchestratorServer) @@ -311,12 +312,12 @@ func startOrchestratorClient(ctx context.Context, uri *url.URL) (net.Orchestrato return c, conn, nil } -func genOrchestratorReq(b common.Broadcaster) (*net.OrchestratorRequest, error) { +func genOrchestratorReq(b common.Broadcaster, caps *net.Capabilities) (*net.OrchestratorRequest, error) { sig, err := b.Sign([]byte(fmt.Sprintf("%v", b.Address().Hex()))) if err != nil { return nil, err } - return &net.OrchestratorRequest{Address: b.Address().Bytes(), Sig: sig}, nil + return &net.OrchestratorRequest{Address: b.Address().Bytes(), Sig: sig, Capabilities: caps}, nil } func genEndSessionRequest(sess *BroadcastSession) (*net.EndTranscodingSessionRequest, error) { @@ -334,7 +335,12 @@ func getOrchestrator(orch Orchestrator, req *net.OrchestratorRequest) (*net.Orch } // currently, orchestrator == transcoder - return orchestratorInfo(orch, addr, orch.ServiceURI().String(), "") + + if req.Capabilities == nil { + return orchestratorInfo(orch, addr, orch.ServiceURI().String(), "") + } + + return orchestratorInfoWithCaps(orch, addr, orch.ServiceURI().String(), "", req.Capabilities) } func endTranscodingSession(node *core.LivepeerNode, orch Orchestrator, req *net.EndTranscodingSessionRequest) (*net.EndTranscodingSessionResponse, error) { @@ -357,9 +363,23 @@ func getPriceInfo(orch Orchestrator, addr ethcommon.Address, manifestID core.Man } func orchestratorInfo(orch Orchestrator, addr ethcommon.Address, serviceURI string, manifestID core.ManifestID) (*net.OrchestratorInfo, error) { - priceInfo, err := getPriceInfo(orch, addr, manifestID) - if err != nil { - return nil, err + return orchestratorInfoWithCaps(orch, addr, serviceURI, manifestID, nil) +} + +func orchestratorInfoWithCaps(orch Orchestrator, addr ethcommon.Address, serviceURI string, manifestID core.ManifestID, caps *net.Capabilities) (*net.OrchestratorInfo, error) { + var priceInfo *net.PriceInfo + if caps == nil { + var err error + priceInfo, err = getPriceInfo(orch, addr, manifestID) + if err != nil { + return nil, err + } + } else { + var err error + priceInfo, err = orch.PriceInfoForCaps(addr, manifestID, caps) + if err != nil { + return nil, err + } } params, err := orch.TicketParams(addr, priceInfo) From 0e4d35a885725ea9a205feeb37f2598a2ef9825f Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Thu, 14 Mar 2024 20:52:12 +0000 Subject: [PATCH 056/203] core: Default transcode max price --- core/orchestrator.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/core/orchestrator.go b/core/orchestrator.go index c810fb5b5..4e7c6800d 100644 --- a/core/orchestrator.go +++ b/core/orchestrator.go @@ -322,12 +322,14 @@ func (orch *orchestrator) priceInfo(sender ethcommon.Address, manifestID Manifes } } + transcodePrice := orch.node.GetBasePrice(sender.String()) + if transcodePrice == nil { + transcodePrice = orch.node.GetBasePrice("default") + } + var basePrice *big.Rat if caps == nil { - basePrice = orch.node.GetBasePrice(sender.String()) - if basePrice == nil { - basePrice = orch.node.GetBasePrice("default") - } + basePrice = transcodePrice } else { // The base price is the sum of the prices of individual capability + model ID pairs basePrice = big.NewRat(0, 1) @@ -349,6 +351,12 @@ func (orch *orchestrator) priceInfo(sender ethcommon.Address, manifestID Manifes } } } + + // If no priced capabilities were signaled by the broadcaster assume that they are requesting + // transcoding and set the base price to the transcode price + if basePrice.Cmp(big.NewRat(0, 1)) == 0 { + basePrice = transcodePrice + } } if !orch.node.AutoAdjustPrice { From f69870816f362c88bd53abf8913290eab38bc054 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Fri, 15 Mar 2024 22:07:35 +0000 Subject: [PATCH 057/203] server: Use frame count for out pixels --- server/ai_http.go | 8 +++++--- server/ai_process.go | 7 ++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/server/ai_http.go b/server/ai_http.go index 31368edc0..d4af86a4d 100644 --- a/server/ai_http.go +++ b/server/ai_http.go @@ -179,8 +179,10 @@ func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request if v.Width != nil { width = int64(*v.Width) } + // The # of frames outputted by stable-video-diffusion-img2vid-xt models + frames := int64(25) - outPixels = height * width + outPixels = height * width * int64(frames) default: respondWithError(w, "Unknown request type", http.StatusBadRequest) return @@ -222,8 +224,8 @@ func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request took := time.Since(start) clog.Infof(ctx, "Processed request id=%v cap=%v modelID=%v took=%v", requestID, cap, modelID, took) - // At the moment, outPixels is expected to just be height * width - // If the # of inference/denoising steps becomes configurable, a possible updated formula could be height * width * steps + // At the moment, outPixels is expected to just be height * width * frames + // If the # of inference/denoising steps becomes configurable, a possible updated formula could be height * width * frames * steps // If additional parameters that influence compute cost become configurable, then the formula should be reconsidered orch.DebitFees(sender, manifestID, payment.GetExpectedPrice(), outPixels) diff --git a/server/ai_process.go b/server/ai_process.go index 996ca8cad..14cb190e2 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -231,8 +231,9 @@ func submitImageToVideo(ctx context.Context, params aiRequestParams, sess *AISes req.Width = new(int) *req.Width = 1024 } + frames := int64(25) - outPixels := int64(*req.Height) * int64(*req.Width) + outPixels := int64(*req.Height) * int64(*req.Width) * frames setHeaders, balUpdate, err := prepareAIPayment(ctx, sess, outPixels) if err != nil { return nil, err @@ -350,8 +351,8 @@ func prepareAIPayment(ctx context.Context, sess *AISession, outPixels int64) (wo return nil, nil, err } - // At the moment, outPixels is expected to just be height * width - // If the # of inference/denoising steps becomes configurable, a possible updated formula could be height * width * steps + // At the moment, outPixels is expected to just be height * width * frames + // If the # of inference/denoising steps becomes configurable, a possible updated formula could be height * width * frames * steps // If additional parameters that influence compute cost become configurable, then the formula should be reconsidered fee, err := estimateAIFee(outPixels, priceInfo) if err != nil { From bd12e99a8e988caec4f80c90525d9fefb2d7e8c9 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Wed, 20 Mar 2024 21:10:02 +0000 Subject: [PATCH 058/203] server: Re-use session logic for AI --- server/ai_session.go | 30 ++++++++++++++++++++++++++++++ server/broadcast.go | 15 +++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/server/ai_session.go b/server/ai_session.go index 7a5930ce5..6274e397d 100644 --- a/server/ai_session.go +++ b/server/ai_session.go @@ -3,6 +3,7 @@ package server import ( "context" "math" + "math/rand" "strconv" "sync" "time" @@ -11,6 +12,7 @@ import ( "github.com/livepeer/go-livepeer/common" "github.com/livepeer/go-livepeer/core" "github.com/livepeer/go-tools/drivers" + "github.com/livepeer/lpms/stream" ) type AISession struct { @@ -25,6 +27,7 @@ type AISession struct { type AISessionPool struct { selector BroadcastSessionsSelector sessMap map[string]*BroadcastSession + inUseSess []*BroadcastSession suspender *suspender mu sync.RWMutex } @@ -44,6 +47,13 @@ func (pool *AISessionPool) Select(ctx context.Context) *BroadcastSession { for { sess := pool.selector.Select(ctx) + if sess == nil { + sess = pool.selectInUse() + } else { + // Track in-use session the first time it is returned by the selector + pool.inUseSess = append(pool.inUseSess, sess) + } + if sess == nil { return nil } @@ -53,6 +63,9 @@ func (pool *AISessionPool) Select(ctx context.Context) *BroadcastSession { continue } + // Track a dummy segment for the session in indicate an in-flight request + sess.pushSegInFlight(&stream.HLSSegment{}) + return sess } } @@ -73,6 +86,14 @@ func (pool *AISessionPool) Complete(sess *BroadcastSession) { return } + // If there are still in-flight requests for the session return early + // and do not return the session to the selector + inFlight, _ := sess.popSegInFlight() + if inFlight > 0 { + return + } + + pool.inUseSess = removeSessionFromList(pool.inUseSess, sess) pool.selector.Complete(sess) } @@ -103,6 +124,7 @@ func (pool *AISessionPool) Remove(sess *BroadcastSession) { defer pool.mu.Unlock() delete(pool.sessMap, sess.Transcoder()) + pool.inUseSess = removeSessionFromList(pool.inUseSess, sess) // Magic number for now penalty := 3 @@ -118,6 +140,14 @@ func (pool *AISessionPool) Size() int { return len(pool.sessMap) } +func (pool *AISessionPool) selectInUse() *BroadcastSession { + if len(pool.inUseSess) == 0 { + return nil + } + // Select a random in-use session + return pool.inUseSess[rand.Intn(len(pool.inUseSess))] +} + type AISessionSelector struct { // Pool of sessions with orchs that have the requested model warm warmPool *AISessionPool diff --git a/server/broadcast.go b/server/broadcast.go index f96b02bfb..bb0a8344f 100755 --- a/server/broadcast.go +++ b/server/broadcast.go @@ -507,6 +507,21 @@ func (bs *BroadcastSession) pushSegInFlight(seg *stream.HLSSegment) { bs.lock.Unlock() } +// Pop a SegFlightMetadata from a session's SegsInFlight +// Returns the end length of a session's SegsInFlight and the popped SegFlightMetadata +func (bs *BroadcastSession) popSegInFlight() (int, SegFlightMetadata) { + bs.lock.Lock() + defer bs.lock.Unlock() + + if len(bs.SegsInFlight) == 0 { + return 0, SegFlightMetadata{} + } + + sm := bs.SegsInFlight[0] + bs.SegsInFlight = bs.SegsInFlight[1:] + return len(bs.SegsInFlight), sm +} + // selects number of sessions to use according to current algorithm func (bsm *BroadcastSessionsManager) selectSessions(ctx context.Context) (bs []*BroadcastSession, calcPerceptualHash bool, verified bool) { bsm.sessLock.Lock() From a818a61f43f47aede8ec16943977d8e99bc3ef37 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Mon, 25 Mar 2024 17:08:55 +0100 Subject: [PATCH 059/203] docs(ai): add AI subnet orch setup guide This commit adds detailed instructions for setting up an orchestrator on the AI Subnet. --- doc/ai-subnet.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 doc/ai-subnet.md diff --git a/doc/ai-subnet.md b/doc/ai-subnet.md new file mode 100644 index 000000000..8c2a5da5e --- /dev/null +++ b/doc/ai-subnet.md @@ -0,0 +1,45 @@ +# AI Subnet Guide + +Welcome to the [Livepeer Artificial Intelligence (AI) Subnet](https://explorer.livepeer.org/treasury/110409521297538895053642752647313688591695822800862508217133236436856613165807) 🤖! This guide will walk you through setting up your Orchestrator to utilize the AI Subnet of the [Livepeer protocol](https://livepeer.org/) and perform AI jobs. + +> [!WARNING] +> The AI Subnet is currently in alpha and under active development. Running it on the same machine as your main Orchestrator is not recommended due to potential stability issues. Proceed with caution and understand the associated risks. + +## Prerequisites + +Before you begin, ensure that you have the following prerequisites installed on your machine: + +- [A Linux-based operating system](https://www.ubuntu.com/download) +- [Docker](https://docs.docker.com/install/) +- [Nvidia Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html) + +> [!NOTE] +> While this guide is tailored for Linux, experienced Docker users can adapt the instructions for Windows or macOS environments. + +## Setup Instructions + +Follow these step-by-step instructions to configure your Livepeer Orchestrator for the AI Subnet: + +1. **Install Docker and Nvidia Container Toolkit**: Install Docker and the Nvidia Container Toolkit on your machine by following the respective installation guides. + +2. **Clone the `ai-video` Branch**: Clone the `ai-video` branch from the [go-livepeer](https://github.com/livepeer/go-livepeer/tree/ai-video) repository. + +3. **Build the Subnet Docker Image**: Navigate to the root of the cloned repository and build the Subnet Docker image using the following command: + + ```bash + docker build -f ./docker/Dockerfile -t livepeer/go-livepeer-ai:latest . + ``` + +4. **Configure AI Models**: Create an `aiModels.json` file in the `~/.lpData` directory to specify the AI models to support in the AI Subnet. Refer to the provided example for details on formatting. + +5. **Download AI Models**: Download the models listed in `aiModels.json` to the `~/.lpData/models` directory using the [ld_checkpoints.sh](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) script from the [livepeer/ai-worker](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) repository. + +6. **Run the Subnet Docker Image**: Execute the following command to run start your AI Subnet Orchestrator: + + ```bash + docker run -v ~/.lpData/:/root/.lpData -v /var/run/docker.sock:/var/run/docker.sock --network host --gpus all livepeer/go-livepeer-ai:latest -orchestrator -transcoder -aiWorker -serviceAddr 0.0.0.0:8936 -v 6 -nvidia "all" -aiModels /root/.lpData/aiModels.json + ``` + +7. **Verify Setup**: Ensure that the AI Subnet Orchestrator runs on port 8936. Open port 8936 on your machine and forward it to the internet for external access. + +That's it! You've successfully configured your Livepeer Orchestrator to perform AI jobs on the AI Subnet. If you encounter any issues or have questions, feel free to reach out to us in the [ai-video channel](https://discord.com/channels/423160867534929930/1187806216185974934) of the Livepeer community on [Discord](https://discord.gg/livepeer). From b3fdf31007926b207ad257eb3576d7966cb0ab01 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Mon, 25 Mar 2024 18:09:43 +0100 Subject: [PATCH 060/203] docs(ai): add ai subnet broadcaster instructions This commit adds detailed instructions for setting up an broadcaster on the AI Subnet. --- doc/ai-subnet.md | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/doc/ai-subnet.md b/doc/ai-subnet.md index 8c2a5da5e..c92aac49a 100644 --- a/doc/ai-subnet.md +++ b/doc/ai-subnet.md @@ -1,9 +1,9 @@ # AI Subnet Guide -Welcome to the [Livepeer Artificial Intelligence (AI) Subnet](https://explorer.livepeer.org/treasury/110409521297538895053642752647313688591695822800862508217133236436856613165807) 🤖! This guide will walk you through setting up your Orchestrator to utilize the AI Subnet of the [Livepeer protocol](https://livepeer.org/) and perform AI jobs. +Welcome to the [Livepeer Artificial Intelligence (AI) Subnet](https://explorer.livepeer.org/treasury/110409521297538895053642752647313688591695822800862508217133236436856613165807) 🤖! This guide will walk you through setting up your Orchestrator/Broadcaster to utilize the AI Subnet of the [Livepeer protocol](https://livepeer.org/) and perform or request AI jobs. > [!WARNING] -> The AI Subnet is currently in alpha and under active development. Running it on the same machine as your main Orchestrator is not recommended due to potential stability issues. Proceed with caution and understand the associated risks. +> The AI Subnet is currently in alpha and under active development. Running it on the same machine as your main Orchestrator/Broadcatser is not recommended due to potential stability issues. Proceed with caution and understand the associated risks. ## Prerequisites @@ -16,9 +16,9 @@ Before you begin, ensure that you have the following prerequisites installed on > [!NOTE] > While this guide is tailored for Linux, experienced Docker users can adapt the instructions for Windows or macOS environments. -## Setup Instructions +## Common Setup Instructions -Follow these step-by-step instructions to configure your Livepeer Orchestrator for the AI Subnet: +Follow these step-by-step instructions to prepare your machine for the AI Subnet: 1. **Install Docker and Nvidia Container Toolkit**: Install Docker and the Nvidia Container Toolkit on your machine by following the respective installation guides. @@ -30,16 +30,38 @@ Follow these step-by-step instructions to configure your Livepeer Orchestrator f docker build -f ./docker/Dockerfile -t livepeer/go-livepeer-ai:latest . ``` -4. **Configure AI Models**: Create an `aiModels.json` file in the `~/.lpData` directory to specify the AI models to support in the AI Subnet. Refer to the provided example for details on formatting. +## Orchestrator Configuration + +Follow these step-by-step instructions to configure your Livepeer Orchestrator for the AI Subnet: + +1. **Configure AI Models**: Create an `aiModels.json` file in the `~/.lpData` directory to specify the AI models to support in the AI Subnet. Refer to the provided example for details on formatting. -5. **Download AI Models**: Download the models listed in `aiModels.json` to the `~/.lpData/models` directory using the [ld_checkpoints.sh](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) script from the [livepeer/ai-worker](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) repository. +2. **Download AI Models**: Download the models listed in `aiModels.json` to the `~/.lpData/models` directory using the [ld_checkpoints.sh](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) script from the [livepeer/ai-worker](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) repository. -6. **Run the Subnet Docker Image**: Execute the following command to run start your AI Subnet Orchestrator: +3. **Run the Subnet Docker Image**: Execute the following command to run start your AI Subnet Orchestrator: ```bash docker run -v ~/.lpData/:/root/.lpData -v /var/run/docker.sock:/var/run/docker.sock --network host --gpus all livepeer/go-livepeer-ai:latest -orchestrator -transcoder -aiWorker -serviceAddr 0.0.0.0:8936 -v 6 -nvidia "all" -aiModels /root/.lpData/aiModels.json ``` -7. **Verify Setup**: Ensure that the AI Subnet Orchestrator runs on port 8936. Open port 8936 on your machine and forward it to the internet for external access. +4. **Verify Setup**: Ensure that the AI Subnet Orchestrator runs on port 8936. Open port 8936 on your machine and forward it to the internet for external access. + +That's it! You've successfully configured your Livepeer Orchestrator to perform AI jobs on the AI Subnet 🚀. + +## Broadcaster Configuration + +Follow these step-by-step instructions to configure your Livepeer Broadcaster for the AI Subnet: + +1. **Run the Subnet Docker Image**: Execute the following command to run start your AI Subnet Broadcaster: + + ```bash + docker run -v ~/.lpData2/:/root/.lpData2 -p 8937:8937 livepeer/go-livepeer-ai:latest -datadir ~/.lpData2 -broadcaster -orchAddr -httpAddr 0.0.0.0:8937 -v 6 -httpIngest + ``` + +2. **Verify Setup**: Ensure that the AI Subnet Broadcaster runs on port 8937. Open port 8937 on your machine and forward it to the internet for external access. + +That's it! You've successfully configured your Livepeer Broadcaster to request AI jobs on the AI Subnet 🚀. + +## Issues -That's it! You've successfully configured your Livepeer Orchestrator to perform AI jobs on the AI Subnet. If you encounter any issues or have questions, feel free to reach out to us in the [ai-video channel](https://discord.com/channels/423160867534929930/1187806216185974934) of the Livepeer community on [Discord](https://discord.gg/livepeer). +If you encounter any issues or have questions, feel free to reach out to us in the [ai-video channel](https://discord.com/channels/423160867534929930/1187806216185974934) of the Livepeer community on [Discord](https://discord.gg/livepeer). From f13b8b0db984482129e5a81f37547fa61a7083d1 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Mon, 25 Mar 2024 20:03:56 +0100 Subject: [PATCH 061/203] docs(ai): enhance AI Subnet documentation with binary installation guide This commit refines the AI Subnet documentation and introduces instructions for utilizing the prebuilt AI Subnet binaries. --- doc/ai-subnet.md | 104 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 74 insertions(+), 30 deletions(-) diff --git a/doc/ai-subnet.md b/doc/ai-subnet.md index c92aac49a..918fe1c8e 100644 --- a/doc/ai-subnet.md +++ b/doc/ai-subnet.md @@ -1,67 +1,111 @@ -# AI Subnet Guide +# AI Subnet Setup Guide -Welcome to the [Livepeer Artificial Intelligence (AI) Subnet](https://explorer.livepeer.org/treasury/110409521297538895053642752647313688591695822800862508217133236436856613165807) 🤖! This guide will walk you through setting up your Orchestrator/Broadcaster to utilize the AI Subnet of the [Livepeer protocol](https://livepeer.org/) and perform or request AI jobs. +Welcome to the [Livepeer Artificial Intelligence (AI) Subnet](https://explorer.livepeer.org/treasury/110409521297538895053642752647313688591695822800862508217133236436856613165807) 🤖! This comprehensive guide will assist you in setting up your Orchestrator/Broadcaster to utilize the AI Subnet of the Livepeer protocol for AI job processing. > [!WARNING] -> The AI Subnet is currently in alpha and under active development. Running it on the same machine as your main Orchestrator/Broadcatser is not recommended due to potential stability issues. Proceed with caution and understand the associated risks. +> Please note that the AI Subnet is currently in alpha and under active development. Running it on the same machine as your main Orchestrator/Broadcaster is not recommended due to potential stability issues. Proceed with caution and understand the associated risks. -## Prerequisites +## Binary Installation -Before you begin, ensure that you have the following prerequisites installed on your machine: +### Prerequisites -- [A Linux-based operating system](https://www.ubuntu.com/download) -- [Docker](https://docs.docker.com/install/) -- [Nvidia Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html) +Before getting started, ensure that your machine meets the following prerequisites: -> [!NOTE] -> While this guide is tailored for Linux, experienced Docker users can adapt the instructions for Windows or macOS environments. +- [Cuda 12](https://developer.nvidia.com/cuda-downloads) -## Common Setup Instructions +### Setup Instructions -Follow these step-by-step instructions to prepare your machine for the AI Subnet: +1. **Obtain the Latest AI Subnet Binary**: Navigate to the [#🪛│builds Channel](https://discord.com/channels/423160867534929930/577736983036559360) in the [Livepeer community Discord](https://discord.com/channels/423160867534929930/577736983036559360). Look for the most recent message that mentions `Branch: ai-video`. This message will contain the latest AI Subnet binaries that are compatible with your system. +2. **Extract and Configure the Binary**: Once downloaded, extract the binary to a directory of your choice. -1. **Install Docker and Nvidia Container Toolkit**: Install Docker and the Nvidia Container Toolkit on your machine by following the respective installation guides. +### Orchestrator Configuration + +1. **AI Model Configuration**: Create an `aiModels.json` file in the `~/.lpData` directory to specify the AI models to support in the AI Subnet. Refer to the provided example below for proper formatting: -2. **Clone the `ai-video` Branch**: Clone the `ai-video` branch from the [go-livepeer](https://github.com/livepeer/go-livepeer/tree/ai-video) repository. + ```json + [ + { + "pipeline": "text-to-image", + "model_id": "stabilityai/sdxl-turbo", + "price_per_unit": 4768371 + }, + { + "pipeline": "text-to-image", + "model_id": "ByteDance/SDXL-Lightning", + "price_per_unit": 4768371, + "warm": true + }, + { + "pipeline": "image-to-video", + "model_id": "stabilityai/stable-video-diffusion-img2vid-xt-1-1", + "price_per_unit": 3390842 + } + ] + ``` -3. **Build the Subnet Docker Image**: Navigate to the root of the cloned repository and build the Subnet Docker image using the following command: +2. **Download AI Models**: Download the models listed in `aiModels.json` to the `~/.lpData/models` directory using the [ld_checkpoints.sh](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) script from the [livepeer/ai-worker](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) repository. +3. **Start the Orchestrator**: Execute the following command to start your AI Subnet Orchestrator: ```bash - docker build -f ./docker/Dockerfile -t livepeer/go-livepeer-ai:latest . + ./livepeer -orchestrator -transcoder -aiWorker -serviceAddr 0.0.0.0:8936 -v 6 -nvidia "all" -aiModels ~/.lpData/aiModels.json ``` -## Orchestrator Configuration +4. **Verify Setup**: Ensure that the AI Subnet Orchestrator runs on port 8936. Open port 8936 on your machine and forward it to the internet for external access. -Follow these step-by-step instructions to configure your Livepeer Orchestrator for the AI Subnet: +### Broadcaster Configuration -1. **Configure AI Models**: Create an `aiModels.json` file in the `~/.lpData` directory to specify the AI models to support in the AI Subnet. Refer to the provided example for details on formatting. +1. **Start the Broadcaster**: Execute the following command to start your AI Subnet Broadcaster: -2. **Download AI Models**: Download the models listed in `aiModels.json` to the `~/.lpData/models` directory using the [ld_checkpoints.sh](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) script from the [livepeer/ai-worker](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) repository. + ```bash + ./livepeer -datadir ~/.lpData2 -broadcaster -orchAddr -httpAddr 0.0.0.0:8937 -v 6 -httpIngest + ``` + +2. **Verify Setup**: Ensure that the AI Subnet Broadcaster runs on port 8937. Open port 8937 on your machine and forward it to the internet for external access. + +## Docker Installation + +### Prerequisites -3. **Run the Subnet Docker Image**: Execute the following command to run start your AI Subnet Orchestrator: +Before proceeding, ensure that your machine satisfies the following prerequisites: + +- [A Linux-based operating system](https://www.ubuntu.com/download) +- [Docker](https://docs.docker.com/install/) +- [Nvidia Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html) + +> [!NOTE] +> Although this guide focuses on Linux, experienced Docker users can adapt the instructions for Windows or macOS environments. + +### Setup Instructions + +1. **Install Docker and Nvidia Container Toolkit**: Install Docker and the Nvidia Container Toolkit on your machine by following the respective installation guides. +2. **Pull the Subnet Docker Image**: Pull the latest Subnet Docker image from the [Livepeer Docker Hub](https://hub.docker.com/r/livepeer/go-livepeer-ai) using the following command: ```bash - docker run -v ~/.lpData/:/root/.lpData -v /var/run/docker.sock:/var/run/docker.sock --network host --gpus all livepeer/go-livepeer-ai:latest -orchestrator -transcoder -aiWorker -serviceAddr 0.0.0.0:8936 -v 6 -nvidia "all" -aiModels /root/.lpData/aiModels.json + docker pull livepeer/go-livepeer:ai-video ``` -4. **Verify Setup**: Ensure that the AI Subnet Orchestrator runs on port 8936. Open port 8936 on your machine and forward it to the internet for external access. +### Orchestrator Configuration + +1. **AI Model Configuration**: Create an `aiModels.json` file in the `~/.lpData` directory to specify the AI models to support in the AI Subnet. Refer to the provided example above for proper formatting. +2. **Download AI Models**: Download the models listed in `aiModels.json` to the `~/.lpData/models` directory using the [ld_checkpoints.sh](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) script from the [livepeer/ai-worker](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) repository. +3. **Run the Subnet Docker Image**: Execute the following command to start your AI Subnet Orchestrator: -That's it! You've successfully configured your Livepeer Orchestrator to perform AI jobs on the AI Subnet 🚀. + ```bash + docker run -v ~/.lpData/:/root/.lpData -v /var/run/docker.sock:/var/run/docker.sock --network host --gpus all livepeer/go-livepeer:ai-video -orchestrator -transcoder -aiWorker -serviceAddr 0.0.0.0:8936 -v 6 -nvidia "all" -aiModels /root/.lpData/aiModels.json + ``` -## Broadcaster Configuration +4. **Verify Setup**: Ensure that the AI Subnet Orchestrator runs on port 8936. Open port 8936 on your machine and forward it to the internet for external access. -Follow these step-by-step instructions to configure your Livepeer Broadcaster for the AI Subnet: +### Broadcaster Configuration -1. **Run the Subnet Docker Image**: Execute the following command to run start your AI Subnet Broadcaster: +1. **Start the Broadcaster**: Execute the following command to start your AI Subnet Broadcaster: ```bash - docker run -v ~/.lpData2/:/root/.lpData2 -p 8937:8937 livepeer/go-livepeer-ai:latest -datadir ~/.lpData2 -broadcaster -orchAddr -httpAddr 0.0.0.0:8937 -v 6 -httpIngest + docker run -v ~/.lpData2/:/root/.lpData2 -p 8937:8937 livepeer/go-livepeer:ai-video -datadir ~/.lpData2 -broadcaster -orchAddr -httpAddr 0.0.0.0:8937 -v 6 -httpIngest ``` 2. **Verify Setup**: Ensure that the AI Subnet Broadcaster runs on port 8937. Open port 8937 on your machine and forward it to the internet for external access. -That's it! You've successfully configured your Livepeer Broadcaster to request AI jobs on the AI Subnet 🚀. - ## Issues If you encounter any issues or have questions, feel free to reach out to us in the [ai-video channel](https://discord.com/channels/423160867534929930/1187806216185974934) of the Livepeer community on [Discord](https://discord.gg/livepeer). From 43f41661ca53ce9712b727f9bd8719a7294a632c Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Wed, 27 Mar 2024 12:51:41 +0100 Subject: [PATCH 062/203] docs(ai): improve ai subnet documentation This commit improves the AI subnet documentation by adding some missing steps and improved unclear steps. Special thanks to @Pon-node for walking through the documentation as I could code along and improve it. Co-authored-by: Pon <94224680+Pon-node@users.noreply.github.com> --- doc/ai-subnet.md | 105 +++++++++++++++++++++++++++++++---------------- 1 file changed, 69 insertions(+), 36 deletions(-) diff --git a/doc/ai-subnet.md b/doc/ai-subnet.md index 918fe1c8e..758f4cc84 100644 --- a/doc/ai-subnet.md +++ b/doc/ai-subnet.md @@ -5,30 +5,29 @@ Welcome to the [Livepeer Artificial Intelligence (AI) Subnet](https://explorer.l > [!WARNING] > Please note that the AI Subnet is currently in alpha and under active development. Running it on the same machine as your main Orchestrator/Broadcaster is not recommended due to potential stability issues. Proceed with caution and understand the associated risks. -## Binary Installation +## Prerequisites -### Prerequisites +Before starting with either the binary or Docker installation, ensure your system meets these requirements: -Before getting started, ensure that your machine meets the following prerequisites: +- [An Nvidia GPU](https://developer.nvidia.com/cuda-gpus) +- [The Nvidia driver](https://www.nvidia.com/Download/index.aspx) +- [Docker](https://docs.docker.com/install/) +- [Nvidia Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html) +- [Cuda 12](https://developer.nvidia.com/cuda-downloads) - Only required for the binary installation. -- [Cuda 12](https://developer.nvidia.com/cuda-downloads) +> [!NOTE] +> Although this guide was tested on Linux, experienced users can adapt the instructions for Windows or macOS environments. -### Setup Instructions +### Orchestrator Setup -1. **Obtain the Latest AI Subnet Binary**: Navigate to the [#🪛│builds Channel](https://discord.com/channels/423160867534929930/577736983036559360) in the [Livepeer community Discord](https://discord.com/channels/423160867534929930/577736983036559360). Look for the most recent message that mentions `Branch: ai-video`. This message will contain the latest AI Subnet binaries that are compatible with your system. -2. **Extract and Configure the Binary**: Once downloaded, extract the binary to a directory of your choice. +#### AI Models Configuration -### Orchestrator Configuration +Orchestrators on the AI subnet can chose which models to support on their machines. To do this: 1. **AI Model Configuration**: Create an `aiModels.json` file in the `~/.lpData` directory to specify the AI models to support in the AI Subnet. Refer to the provided example below for proper formatting: ```json [ - { - "pipeline": "text-to-image", - "model_id": "stabilityai/sdxl-turbo", - "price_per_unit": 4768371 - }, { "pipeline": "text-to-image", "model_id": "ByteDance/SDXL-Lightning", @@ -43,51 +42,69 @@ Before getting started, ensure that your machine meets the following prerequisit ] ``` -2. **Download AI Models**: Download the models listed in `aiModels.json` to the `~/.lpData/models` directory using the [ld_checkpoints.sh](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) script from the [livepeer/ai-worker](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) repository. -3. **Start the Orchestrator**: Execute the following command to start your AI Subnet Orchestrator: +2. **Install Hugging Face CLI**: Install the Hugging Face CLI by running the following command: ```bash - ./livepeer -orchestrator -transcoder -aiWorker -serviceAddr 0.0.0.0:8936 -v 6 -nvidia "all" -aiModels ~/.lpData/aiModels.json + pip install huggingface_hub[cli,hf_transfer] ``` -4. **Verify Setup**: Ensure that the AI Subnet Orchestrator runs on port 8936. Open port 8936 on your machine and forward it to the internet for external access. +3. **Create a Hugging Face Access Token**: Create a Hugging Face access token by fallowing the instructions in the [Hugging Face documentation](https://huggingface.co/docs/hf-cli/auth/) and make this token available under the `HG_TOKEN` environment variable. This token will be used to download token-gated models from the Hugging Face model hub. -### Broadcaster Configuration + > [!IMPORTANT] + > The `ld_checkpoints.sh` script contains the [SVD1.1](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt-1-1) model which currently also requires you to agree to the model's license agreement. If you want to advertice this models on the AI Subnet you need to go to the [model page](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt-1-1) login and accept their terms. -1. **Start the Broadcaster**: Execute the following command to start your AI Subnet Broadcaster: +4. **Download AI Models**: Download the models listed in `aiModels.json` to the `~/.lpData/models` directory using the [ld_checkpoints.sh](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) script from the [livepeer/ai-worker](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) repository. You can run the following in your terminal to do this: ```bash - ./livepeer -datadir ~/.lpData2 -broadcaster -orchAddr -httpAddr 0.0.0.0:8937 -v 6 -httpIngest + curl -s https://raw.githubusercontent.com/livepeer/ai-worker/main/runner/dl_checkpoints.sh | bash -s --alpha ``` -2. **Verify Setup**: Ensure that the AI Subnet Broadcaster runs on port 8937. Open port 8937 on your machine and forward it to the internet for external access. + > [!IMPORTANT] + > The `--alpha` flag is used to only download the models that are currently supported by the Livepeer.inc gateway node on the AI Subnet. If you want to download all models and advertice them for other gateway nodes, you can remove this flag. -## Docker Installation +#### Orchestrator Binary Setup -### Prerequisites +#### Orchestrator Offchain Binary Setup -Before proceeding, ensure that your machine satisfies the following prerequisites: +To run the AI Subnet Orchestrator off-chain using the pre-build binaries, follow these steps: -- [A Linux-based operating system](https://www.ubuntu.com/download) -- [Docker](https://docs.docker.com/install/) -- [Nvidia Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html) +1. **Obtain the Latest AI Subnet Binary**: Navigate to the [#🪛│builds Channel](https://discord.com/channels/423160867534929930/577736983036559360) in the [Livepeer community Discord](https://discord.com/channels/423160867534929930/577736983036559360). Look for the most recent message that mentions `Branch: ai-video`. This message will contain the latest AI Subnet binaries that are compatible with your system. +2. **Extract and Configure the Binary**: Once downloaded, extract the binary to a directory of your choice. +3. **Pull the Latest AI Runner docker image**: The Livepeer AI Subnet uses a containerized workflow to run the AI models. You can download the latest AI Runner container by running the following command: -> [!NOTE] -> Although this guide focuses on Linux, experienced Docker users can adapt the instructions for Windows or macOS environments. + ```bash + docker pull livepeer/ai-runner:latest + ``` + +4. **Start the Orchestrator**: Execute the following command to start your AI Subnet Orchestrator: + + ```bash + ./livepeer -orchestrator -transcoder -aiWorker -serviceAddr 0.0.0.0:8936 -v 6 -nvidia "all" -aiModels ~/.lpData/aiModels.json + ``` + +5. **Verify Setup**: Ensure that the AI Subnet Orchestrator runs on port 8936. Open port 8936 on your machine and forward it to the internet for external access. + +> [!NOTE] +> If no binaries are available for your system, you can build the [ai-video branch](https://github.com/livepeer/go-livepeer/tree/ai-video) of [go-livepeer](https://github.com/livepeer/go-livepeer) from source by following the instructions in the [Livepeer repository](https://docs.livepeer.org/orchestrators/guides/install-go-livepeer) or by reaching out to the Livepeer community on [Discord](https://discord.gg/livepeer). + +#### Orchestrator Docker Setup + +##### Offchain Orchestrator Docker Setup -### Setup Instructions +To run the AI Subnet Orchestrator using Docker, follow these steps: -1. **Install Docker and Nvidia Container Toolkit**: Install Docker and the Nvidia Container Toolkit on your machine by following the respective installation guides. -2. **Pull the Subnet Docker Image**: Pull the latest Subnet Docker image from the [Livepeer Docker Hub](https://hub.docker.com/r/livepeer/go-livepeer-ai) using the following command: +1. **Pull the Subnet Docker Image**: Pull the latest Subnet Docker image from the [Livepeer Docker Hub](https://hub.docker.com/r/livepeer/go-livepeer-ai) using the following command: ```bash docker pull livepeer/go-livepeer:ai-video ``` -### Orchestrator Configuration +2. **Pull the Latest AI Runner docker image**: The Livepeer AI Subnet uses a containerized workflow to run the AI models. You can download the latest AI Runner container by running the following command: + + ```bash + docker pull livepeer/ai-runner:latest + ``` -1. **AI Model Configuration**: Create an `aiModels.json` file in the `~/.lpData` directory to specify the AI models to support in the AI Subnet. Refer to the provided example above for proper formatting. -2. **Download AI Models**: Download the models listed in `aiModels.json` to the `~/.lpData/models` directory using the [ld_checkpoints.sh](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) script from the [livepeer/ai-worker](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) repository. 3. **Run the Subnet Docker Image**: Execute the following command to start your AI Subnet Orchestrator: ```bash @@ -96,7 +113,23 @@ Before proceeding, ensure that your machine satisfies the following prerequisite 4. **Verify Setup**: Ensure that the AI Subnet Orchestrator runs on port 8936. Open port 8936 on your machine and forward it to the internet for external access. -### Broadcaster Configuration +### Broadcaster Setup + +#### Broadcaster Binary Setup + +##### Offchain Broadcaster Binary Setup + +1. **Start the Broadcaster**: Execute the following command to start your AI Subnet Broadcaster: + + ```bash + ./livepeer -datadir ~/.lpData2 -broadcaster -orchAddr -httpAddr 0.0.0.0:8937 -v 6 -httpIngest + ``` + +2. **Verify Setup**: Ensure that the AI Subnet Broadcaster runs on port 8937. Open port 8937 on your machine and forward it to the internet for external access. + +#### Broadcaster Docker Setup + +##### Offchain Broadcaster Docker Setup 1. **Start the Broadcaster**: Execute the following command to start your AI Subnet Broadcaster: From be3afe85f5a46f2cf456a687f96a7b1ed646f0a5 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Wed, 27 Mar 2024 16:41:09 +0100 Subject: [PATCH 063/203] docs(ai): fix broken huggingface documentation link This commit fixes a broken huggingface documentation link. Co-authored-by: papabear99 <89408276+papabear99@users.noreply.github.com> --- doc/ai-subnet.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ai-subnet.md b/doc/ai-subnet.md index 758f4cc84..b4a0a3f09 100644 --- a/doc/ai-subnet.md +++ b/doc/ai-subnet.md @@ -48,7 +48,7 @@ Orchestrators on the AI subnet can chose which models to support on their machin pip install huggingface_hub[cli,hf_transfer] ``` -3. **Create a Hugging Face Access Token**: Create a Hugging Face access token by fallowing the instructions in the [Hugging Face documentation](https://huggingface.co/docs/hf-cli/auth/) and make this token available under the `HG_TOKEN` environment variable. This token will be used to download token-gated models from the Hugging Face model hub. +3. **Create a Hugging Face Access Token**: Create a Hugging Face access token by fallowing the instructions in the [Hugging Face documentation](https://huggingface.co/docs/hub/en/security-tokens) and make this token available under the `HG_TOKEN` environment variable. This token will be used to download [token-gated models](https://huggingface.co/docs/transformers.js/en/guides/private) from the Hugging Face model hub. > [!IMPORTANT] > The `ld_checkpoints.sh` script contains the [SVD1.1](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt-1-1) model which currently also requires you to agree to the model's license agreement. If you want to advertice this models on the AI Subnet you need to go to the [model page](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt-1-1) login and accept their terms. From c7130b43473a64222db9e92a8d43868ed2973f51 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Wed, 27 Mar 2024 22:18:52 +0100 Subject: [PATCH 064/203] docs(ai): fix broken 'dl_checkpoint' command This commit fixes the broken `dl_checkpoints.sh` run command found in the documentation. Co-authored-by: papabear99 <89408276+papabear99@users.noreply.github.com> --- doc/ai-subnet.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/ai-subnet.md b/doc/ai-subnet.md index b4a0a3f09..d9e3ee809 100644 --- a/doc/ai-subnet.md +++ b/doc/ai-subnet.md @@ -22,7 +22,7 @@ Before starting with either the binary or Docker installation, ensure your syste #### AI Models Configuration -Orchestrators on the AI subnet can chose which models to support on their machines. To do this: +Orchestrators on the AI subnet can choose which models to support on their machines. To do this: 1. **AI Model Configuration**: Create an `aiModels.json` file in the `~/.lpData` directory to specify the AI models to support in the AI Subnet. Refer to the provided example below for proper formatting: @@ -48,19 +48,19 @@ Orchestrators on the AI subnet can chose which models to support on their machin pip install huggingface_hub[cli,hf_transfer] ``` -3. **Create a Hugging Face Access Token**: Create a Hugging Face access token by fallowing the instructions in the [Hugging Face documentation](https://huggingface.co/docs/hub/en/security-tokens) and make this token available under the `HG_TOKEN` environment variable. This token will be used to download [token-gated models](https://huggingface.co/docs/transformers.js/en/guides/private) from the Hugging Face model hub. +3. **Create a Hugging Face Access Token**: Follow the instructions in the [Hugging Face documentation](https://huggingface.co/docs/hub/en/security-tokens) to create a Hugging Face access token and make it available under the `HG_TOKEN` environment variable. This token will download [token-gated models](https://huggingface.co/docs/transformers.js/en/guides/private) from the Hugging Face model hub. Alternatively, you can also install your Hugging Face access token on your machine using [login command](https://huggingface.co/docs/huggingface_hub/en/guides/cli#huggingface-cli-login) of the Hugging Face CLI. - > [!IMPORTANT] - > The `ld_checkpoints.sh` script contains the [SVD1.1](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt-1-1) model which currently also requires you to agree to the model's license agreement. If you want to advertice this models on the AI Subnet you need to go to the [model page](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt-1-1) login and accept their terms. + > [!IMPORTANT] + > The `ld_checkpoints.sh` script contains the [SVD1.1](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt-1-1) model, which currently also requires you to agree to the model's license agreement. If you want to advertise this model on the AI Subnet, you need to go to the [model page](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt-1-1), log in, and accept their terms. 4. **Download AI Models**: Download the models listed in `aiModels.json` to the `~/.lpData/models` directory using the [ld_checkpoints.sh](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) script from the [livepeer/ai-worker](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) repository. You can run the following in your terminal to do this: ```bash - curl -s https://raw.githubusercontent.com/livepeer/ai-worker/main/runner/dl_checkpoints.sh | bash -s --alpha + curl -s https://raw.githubusercontent.com/livepeer/ai-worker/main/runner/dl_checkpoints.sh | bash -s -- --alpha ``` > [!IMPORTANT] - > The `--alpha` flag is used to only download the models that are currently supported by the Livepeer.inc gateway node on the AI Subnet. If you want to download all models and advertice them for other gateway nodes, you can remove this flag. + > The `--alpha` flag is used to download only the models currently supported by the Livepeer.inc gateway node on the AI Subnet. You can remove this flag if you want to download all models and advertise them for other gateway nodes. #### Orchestrator Binary Setup @@ -68,7 +68,7 @@ Orchestrators on the AI subnet can chose which models to support on their machin To run the AI Subnet Orchestrator off-chain using the pre-build binaries, follow these steps: -1. **Obtain the Latest AI Subnet Binary**: Navigate to the [#🪛│builds Channel](https://discord.com/channels/423160867534929930/577736983036559360) in the [Livepeer community Discord](https://discord.com/channels/423160867534929930/577736983036559360). Look for the most recent message that mentions `Branch: ai-video`. This message will contain the latest AI Subnet binaries that are compatible with your system. +1. **Obtain the Latest AI Subnet Binary**: Navigate to the [#🪛│builds Channel](https://discord.com/channels/423160867534929930/577736983036559360) in the [Livepeer community Discord](https://discord.com/channels/423160867534929930/577736983036559360). Look for the most recent message that mentions `Branch: ai-video`. This message will contain the latest AI Subnet binaries compatible with your system. 2. **Extract and Configure the Binary**: Once downloaded, extract the binary to a directory of your choice. 3. **Pull the Latest AI Runner docker image**: The Livepeer AI Subnet uses a containerized workflow to run the AI models. You can download the latest AI Runner container by running the following command: @@ -82,7 +82,7 @@ To run the AI Subnet Orchestrator off-chain using the pre-build binaries, follow ./livepeer -orchestrator -transcoder -aiWorker -serviceAddr 0.0.0.0:8936 -v 6 -nvidia "all" -aiModels ~/.lpData/aiModels.json ``` -5. **Verify Setup**: Ensure that the AI Subnet Orchestrator runs on port 8936. Open port 8936 on your machine and forward it to the internet for external access. +5. **Verify Setup**: Ensure the AI Subnet Orchestrator runs on port 8936. Open port 8936 on your machine and forward it to the internet for external access. > [!NOTE] > If no binaries are available for your system, you can build the [ai-video branch](https://github.com/livepeer/go-livepeer/tree/ai-video) of [go-livepeer](https://github.com/livepeer/go-livepeer) from source by following the instructions in the [Livepeer repository](https://docs.livepeer.org/orchestrators/guides/install-go-livepeer) or by reaching out to the Livepeer community on [Discord](https://discord.gg/livepeer). From 732bf36356b8a2def0a7f742664af19724fb4111 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 28 Mar 2024 09:54:51 +0100 Subject: [PATCH 065/203] docs(ai): enhance clarity and accuracy of AI subnet docs This commit refines the AI subnet documentation to address ambiguities and inaccuracies identified during our initial pilot with **pre-alpha** participants. The improvements aim to provide clearer instructions and correct information for a smoother user experience. --- doc/ai-subnet.md | 212 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 179 insertions(+), 33 deletions(-) diff --git a/doc/ai-subnet.md b/doc/ai-subnet.md index d9e3ee809..325708794 100644 --- a/doc/ai-subnet.md +++ b/doc/ai-subnet.md @@ -1,28 +1,62 @@ # AI Subnet Setup Guide -Welcome to the [Livepeer Artificial Intelligence (AI) Subnet](https://explorer.livepeer.org/treasury/110409521297538895053642752647313688591695822800862508217133236436856613165807) 🤖! This comprehensive guide will assist you in setting up your Orchestrator/Broadcaster to utilize the AI Subnet of the Livepeer protocol for AI job processing. +Welcome to the [Livepeer Artificial Intelligence (AI) Subnet](https://explorer.livepeer.org/treasury/110409521297538895053642752647313688591695822800862508217133236436856613165807) 🤖! This comprehensive guide will assist you in setting up your Orchestrator or Gateway (formerly called Broadcaster) to utilize the AI Subnet of the [Livepeer protocol](https://livepeer.org/) for AI job processing. -> [!WARNING] -> Please note that the AI Subnet is currently in alpha and under active development. Running it on the same machine as your main Orchestrator/Broadcaster is not recommended due to potential stability issues. Proceed with caution and understand the associated risks. +> [!ATTENTION] +> Please note that the AI Subnet is currently in **alpha** and under active development. Running it on the same machine as your main Orchestrator/Gateway node is not recommended due to potential stability issues. Proceed with caution and understand the associated risks. + +## Background + + + +### Supported AI Models + +During the **alpha** and **beta** phases, the AI Subnet supports a limited number of AI models per inference pipeline. The currently supported models per pipeline are: + +**Text-to-Image**: + +- [sd-turbo](https://huggingface.co/stabilityai/sd-turbo) +- [sdxl-turbo](https://huggingface.co/stabilityai/sdxl-turbo) +- [stable-diffusion-v1-5](https://huggingface.co/runwayml/stable-diffusion-v1-5) +- [stable-diffusion-xl-base-1.0](https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0) +- [openjourney-v4](https://huggingface.co/prompthero/openjourney-v4) +- [ByteDance/SDXL-Lightning](https://huggingface.co/ByteDance/SDXL-Lightning) + +**Image-to-Image**: + +- [sd-turbo](https://huggingface.co/stabilityai/sd-turbo) +- [sdxl-turbo](https://huggingface.co/stabilityai/sdxl-turbo) +- [ByteDance/SDXL-Lightning](https://huggingface.co/ByteDance/SDXL-Lightning) + +**Image-to-Video**: + +- [stable-video-diffusion-img2vid-xt](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt) +- [stabilityai/stable-video-diffusion-img2vid-xt-1-1](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt-1-1) + +When the AI subnet is fully operational we plan to support any ([diffusion](https://huggingface.co/docs/diffusers/en/index)) model that can be run in a docker container. ## Prerequisites -Before starting with either the binary or Docker installation, ensure your system meets these requirements: +Before starting with either the binary or Docker installation for the AI subnet, ensure your system meets these requirements: - [An Nvidia GPU](https://developer.nvidia.com/cuda-gpus) - [The Nvidia driver](https://www.nvidia.com/Download/index.aspx) - [Docker](https://docs.docker.com/install/) - [Nvidia Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html) -- [Cuda 12](https://developer.nvidia.com/cuda-downloads) - Only required for the binary installation. +- [Cuda 12](https://developer.nvidia.com/cuda-downloads) (only required for the binary installation) > [!NOTE] > Although this guide was tested on Linux, experienced users can adapt the instructions for Windows or macOS environments. +## Off-chain Setup + +For testing and development purposes, it's a good practice to first run the Orchestrator and Gateway nodes **off-chain**. This allows you to quickly test the AI Subnet and ensure that your Orchestrator and Gateway are functioning correctly before connecting them to the on-chain [Livepeer protocol](https://livepeer.org/). + ### Orchestrator Setup #### AI Models Configuration -Orchestrators on the AI subnet can choose which models to support on their machines. To do this: +Orchestrators on the AI subnet can select the [supported models](#supported-ai-models) they wish to advertise and process. To do this: 1. **AI Model Configuration**: Create an `aiModels.json` file in the `~/.lpData` directory to specify the AI models to support in the AI Subnet. Refer to the provided example below for proper formatting: @@ -34,6 +68,11 @@ Orchestrators on the AI subnet can choose which models to support on their machi "price_per_unit": 4768371, "warm": true }, + { + "pipeline": "text-to-image", + "model_id": "ByteDance/SDXL-Lightning", + "price_per_unit": 4768371 + }, { "pipeline": "image-to-video", "model_id": "stabilityai/stable-video-diffusion-img2vid-xt-1-1", @@ -59,16 +98,14 @@ Orchestrators on the AI subnet can choose which models to support on their machi curl -s https://raw.githubusercontent.com/livepeer/ai-worker/main/runner/dl_checkpoints.sh | bash -s -- --alpha ``` - > [!IMPORTANT] - > The `--alpha` flag is used to download only the models currently supported by the Livepeer.inc gateway node on the AI Subnet. You can remove this flag if you want to download all models and advertise them for other gateway nodes. + > [!NOTE] + > The `--alpha` flag is used to download only the models currently supported by the Livepeer.inc Gateway node on the AI Subnet. You can remove this flag if you want to download all models and advertise them for other Gateway nodes. #### Orchestrator Binary Setup -#### Orchestrator Offchain Binary Setup - -To run the AI Subnet Orchestrator off-chain using the pre-build binaries, follow these steps: +To run the AI Subnet Orchestrator **off-chain** using the [pre-build binaries](https://discord.com/channels/423160867534929930/577736983036559360), follow these steps: -1. **Obtain the Latest AI Subnet Binary**: Navigate to the [#🪛│builds Channel](https://discord.com/channels/423160867534929930/577736983036559360) in the [Livepeer community Discord](https://discord.com/channels/423160867534929930/577736983036559360). Look for the most recent message that mentions `Branch: ai-video`. This message will contain the latest AI Subnet binaries compatible with your system. +1. **Download the Latest AI Subnet Binary**: Visit the [#🪛│builds Channel](https://discord.com/channels/423160867534929930/577736983036559360) on the [Livepeer community Discord](https://discord.com/channels/423160867534929930/577736983036559360). Find the latest message mentioning `Branch: ai-video` and your platform under `Platform:`. This message includes the newest AI Subnet binaries for your system. 2. **Extract and Configure the Binary**: Once downloaded, extract the binary to a directory of your choice. 3. **Pull the Latest AI Runner docker image**: The Livepeer AI Subnet uses a containerized workflow to run the AI models. You can download the latest AI Runner container by running the following command: @@ -76,68 +113,177 @@ To run the AI Subnet Orchestrator off-chain using the pre-build binaries, follow docker pull livepeer/ai-runner:latest ``` -4. **Start the Orchestrator**: Execute the following command to start your AI Subnet Orchestrator: +4. **Start the Orchestrator**: Run the following command to initiate your AI Subnet Orchestrator: ```bash - ./livepeer -orchestrator -transcoder -aiWorker -serviceAddr 0.0.0.0:8936 -v 6 -nvidia "all" -aiModels ~/.lpData/aiModels.json + ./livepeer \ + -orchestrator \ + -transcoder \ + -serviceAddr 0.0.0.0:8936 \ + -v 6 \ + -nvidia "all" \ + -aiWorker \ + -aiModels ~/.lpData/aiModels.json \ + -aiModelsDir ~/.lpData/models ``` -5. **Verify Setup**: Ensure the AI Subnet Orchestrator runs on port 8936. Open port 8936 on your machine and forward it to the internet for external access. + While most of these flags are already used in the [Mainnet transcoding network](https://github.com/livepeer/go-livepeer) (documented in the [Livepeer documentation](https://docs.livepeer.org/references/go-livepeer/cli-reference)), the `-aiWorker`, `-aiModels`, and `-aiModelsDir` flags are new. They enable the AI Subnet Orchestrator, define the location of your AI models configuration, and specify the directory where the models are stored on your machine, respectively. If the `aiModelsDir` flag is not set, the AI Subnet Orchestrator will look for the models in the `~/.lpData//models` directory. + +5. **Verify Setup**: Ensure the AI Subnet Orchestrator runs on port `8936`. Open port `8936` on your machine and forward it to the internet for external access. > [!NOTE] > If no binaries are available for your system, you can build the [ai-video branch](https://github.com/livepeer/go-livepeer/tree/ai-video) of [go-livepeer](https://github.com/livepeer/go-livepeer) from source by following the instructions in the [Livepeer repository](https://docs.livepeer.org/orchestrators/guides/install-go-livepeer) or by reaching out to the Livepeer community on [Discord](https://discord.gg/livepeer). #### Orchestrator Docker Setup -##### Offchain Orchestrator Docker Setup + -To run the AI Subnet Orchestrator using Docker, follow these steps: +To run the AI Subnet Orchestrator **off-chain** using Docker, follow these steps: -1. **Pull the Subnet Docker Image**: Pull the latest Subnet Docker image from the [Livepeer Docker Hub](https://hub.docker.com/r/livepeer/go-livepeer-ai) using the following command: +1. **Pull the AI Subnet Docker Image**: Pull the latest AI subnet Docker image from the [Livepeer Docker Hub](https://hub.docker.com/r/livepeer/go-livepeer-ai) using the following command: ```bash docker pull livepeer/go-livepeer:ai-video ``` -2. **Pull the Latest AI Runner docker image**: The Livepeer AI Subnet uses a containerized workflow to run the AI models. You can download the latest AI Runner container by running the following command: +2. **Pull the Latest AI Runner docker image**: The Livepeer AI Subnet uses a [containerized workflow](https://www.ibm.com/topics/containerization) to run the AI models. You can download the latest [AI Runner](https://hub.docker.com/r/livepeer/ai-runner) image by running the following command: ```bash docker pull livepeer/ai-runner:latest ``` -3. **Run the Subnet Docker Image**: Execute the following command to start your AI Subnet Orchestrator: +3. **Run the AI Subnet Docker Image**: Execute the following command to start your AI Subnet Orchestrator: ```bash - docker run -v ~/.lpData/:/root/.lpData -v /var/run/docker.sock:/var/run/docker.sock --network host --gpus all livepeer/go-livepeer:ai-video -orchestrator -transcoder -aiWorker -serviceAddr 0.0.0.0:8936 -v 6 -nvidia "all" -aiModels /root/.lpData/aiModels.json + docker run \ + -v ~/.lpData/:/root/.lpData/ \ + -v /var/run/docker.sock:/var/run/docker.sock \ + --network host \ + --gpus all \ + livepeer/go-livepeer:ai-video \ + -orchestrator \ + -transcoder \ + -serviceAddr 0.0.0.0:8936 \ + -v 6 \ + -nvidia "all" \ + -aiWorker \ + -aiModels /root/.lpData/aiModels.json \ + -aiModelsDir ~/.lpData/models ``` -4. **Verify Setup**: Ensure that the AI Subnet Orchestrator runs on port 8936. Open port 8936 on your machine and forward it to the internet for external access. + As outlined in the [Orchestrator Binary Setup](#orchestrator-binary-setup), the `-aiWorker`, `-aiModels`, and `-aiModelsDir` flags are unique to the AI Subnet Orchestrator. The remaining flags are common to the [Mainnet transcoding network](https://github.com/livepeer/go-livepeer) as detailed in the [Livepeer documentation](https://docs.livepeer.org/references/go-livepeer/cli-reference). The AI-specific flags activate the AI Subnet Orchestrator, specify the location of your AI models configuration, and define the directory for model storage on your machine. If `aiModelsDir` is not set, the AI Subnet Orchestrator defaults to the `~/.lpData//models` directory for model storage. + +4. **Verify Setup**: Ensure that the AI Subnet Orchestrator runs on port `8936`. Open port `8936` on your machine and forward it to the internet for external access. + +### Gateway Setup + +#### Gateway Binary Setup + +Gateway nodes on the AI subnet can be set up using the [pre-built binaries](https://discord.com/channels/423160867534929930/577736983036559360). Follow these steps to run the AI Subnet Gateway node **off-chain**: + +1. **Download and extract the latest AI Subnet Binary**: Follow steps 1 and 2 from the [Orchestrator Binary Setup](#orchestrator-binary-setup) to download and extract the latest AI Subnet binary for your system. +2. **Start the Gateway**: Execute the following command to start your AI Subnet Gateway node: -### Broadcaster Setup + ```bash + ./livepeer \ + -datadir ~/.lpData2 \ + -broadcaster \ + -orchAddr \ + -httpAddr 0.0.0.0:8937 \ + -v 6 \ + -httpIngest + ``` -#### Broadcaster Binary Setup + The flags used here are also applicable to the [Mainnet transcoding network](https://github.com/livepeer/go-livepeer). For a comprehensive understanding of these flags, consult the [Livepeer documentation](https://docs.livepeer.org/references/go-livepeer/cli-reference). Specifically, the `--orchAddr` and `--httpAddr` flags are crucial for routing the Gateway node to your local Orchestrator (i.e., `0.0.0.0:8936`) and facilitating off-chain communication between the Gateway and the Orchestrator. -##### Offchain Broadcaster Binary Setup +3. **Verify Setup**: Ensure that the AI Subnet Gateway node runs on port `8937`. Open port `8937` on your machine and forward it to the internet for external access. -1. **Start the Broadcaster**: Execute the following command to start your AI Subnet Broadcaster: +#### Gateway Docker Setup + +1. **Pull the AI Subnet Docker Image**: Follow step 1 from the [Orchestrator Docker Setup](#orchestrator-docker-setup) to pull the latest AI Subnet Docker image from the [Livepeer Docker Hub](https://hub.docker.com/r/livepeer/go-livepeer-ai). +2. **Run the AI Subnet Docker Image**: Execute the following command to start your AI Subnet Gateway node: ```bash - ./livepeer -datadir ~/.lpData2 -broadcaster -orchAddr -httpAddr 0.0.0.0:8937 -v 6 -httpIngest + docker run -v ~/.lpData2/:/root/.lpData2 -p 8937:8937 --network host livepeer/go-livepeer:ai-video -datadir ~/.lpData2 -broadcaster -orchAddr -httpAddr 0.0.0.0:8937 -v 6 -httpIngest ``` -2. **Verify Setup**: Ensure that the AI Subnet Broadcaster runs on port 8937. Open port 8937 on your machine and forward it to the internet for external access. + As outlined in the [Gateway Binary Setup](#gateway-binary-setup) the flags are common to the [Mainnet transcoding network](https://github.com/livepeer/go-livepeer) and are documented in the [Livepeer documentation](https://docs.livepeer.org/references/go-livepeer/cli-reference). The `--orchAddr` and `--httpAddr` flags are essential for directing the Gateway node to your local Orchestrator and ensuring **off-chain** communication between the Gateway and the Orchestrator, respectively. + +3. **Verify Setup**: Ensure that the AI Subnet Gateway node runs on port `8937`. Open port `8937` on your machine and forward it to the internet for external access. -#### Broadcaster Docker Setup +#### AI Job Submission -##### Offchain Broadcaster Docker Setup +To verify the correct functioning of your **off-chain** Gateway and Orchestrator nodes, submit an AI inference job for each of the supported pipelines. -1. **Start the Broadcaster**: Execute the following command to start your AI Subnet Broadcaster: +**Text-to-Image Inference Job**: + +To send an `text-to-image` inference job to the Gateway node and receive the result, follow these steps: + +1. **Job Submission**: Submit a job using the `curl` command: ```bash - docker run -v ~/.lpData2/:/root/.lpData2 -p 8937:8937 livepeer/go-livepeer:ai-video -datadir ~/.lpData2 -broadcaster -orchAddr -httpAddr 0.0.0.0:8937 -v 6 -httpIngest + curl -X POST 0.0.0.0:8937/text-to-image -d '{"prompt":"a dog","model_id":"ByteDance/SDXL-Lightning"}' ``` -2. **Verify Setup**: Ensure that the AI Subnet Broadcaster runs on port 8937. Open port 8937 on your machine and forward it to the internet for external access. + The output should look like: + + ```bash + {"images":[{"seed":280278971,"url":"/stream/34937c31/dc88c7c9.png"}]} + ``` + +2. **Result Retrieval**: After job completion, you'll receive a JSON with a URL to download the result. Use `curl` to download: + + ```bash + curl -O 0.0.0.0:8937/stream/34937c31/dc88c7c9.png + ``` + +Congratulations! You've successfully set up your **off-chain** AI Subnet Orchestrator and Gateway nodes to process `text-to-image` inference jobs 🎉. You can repeat the process for the `image-to-video` and `image-to-image` pipelines described below to ensure the correct functioning of all the AI inference pipelines you did setup in your `aiModels.json`. + +**Image-to-Image Inference Job**: + +To send an `image-to-image` inference job to the Gateway node and receive the result, follow these steps: + +1. **Job Submission**: Submit a job using the `curl` command: + + ```bash + curl -X POST 0.0.0.0:8937/image-to-image -F image=@ -F prompt="a dog" -F model_id="ByteDance/SDXL-Lightning" + ``` + + > [!NOTE] + > Substitute `` with the **local path** of the image you want to use for video generation. This command employs [curl](https://curl.se/docs/manpage.html)'s `-F` flag to upload the image file to the Gateway node. Refer to the [curl documentation](https://curl.se/docs/manpage.html) for more details. + +2. **Result Retrieval**: After job completion, you'll receive a JSON with a URL to download the result. Use `curl` to download: + + ```bash + curl -O 0.0.0.0:8937/stream/dffff04c/6f247287.png + ``` + +**Image-to-Video Inference Job**: + +To send an `image-to-video` inference job to the Gateway node and receive the result, follow these steps: + +1. **Job Submission**: Submit a job using the `curl` command: + + ```bash + curl -X POST localhost:8936/image-to-video -F image=@ -F model_id=stabilityai/stable-video-diffusion-img2vid-xt-1- + ``` + + > [!NOTE] + > Substitute `` with the **local path** of the image you want to use for video generation. This command employs [curl](https://curl.se/docs/manpage.html)'s `-F` flag to upload the image file to the Gateway node. Refer to the [curl documentation](https://curl.se/docs/manpage.html) for more details. + +2. **Result Retrieval**: After job completion, you'll receive a JSON with a URL to download the result. Use `curl` to download: + + ```bash + curl -O 0.0.0.0:8937/stream/dffff04c/6f247287.png + ``` + +% TODO: Done till here. + +## On-chain Setup + +#### Put Orchestrator On-chain + +There are two ways to put your AI subnet orchestrator **on-chain** to receive jobs. First you can create a new ETH wallet for your AI subnet orchestrator, deposit some eth for gas fees and set the `ethOrchAddr` flag and set it to the Ethereum address of your orchestrator Alternatively, you can run a ticket redemption service on your main orchestrator to redeem tickets on behalf of your AI subnet orchestrator. ## Issues From 6895dcad0f458a34ec59e27276b4218e48080f00 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 28 Mar 2024 10:32:27 +0100 Subject: [PATCH 066/203] docs(ai): improve model configuration documentation This commit explaines the parameters found in the `aiModels.json` file. --- doc/ai-subnet.md | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/doc/ai-subnet.md b/doc/ai-subnet.md index 325708794..f93ab7d9d 100644 --- a/doc/ai-subnet.md +++ b/doc/ai-subnet.md @@ -2,7 +2,7 @@ Welcome to the [Livepeer Artificial Intelligence (AI) Subnet](https://explorer.livepeer.org/treasury/110409521297538895053642752647313688591695822800862508217133236436856613165807) 🤖! This comprehensive guide will assist you in setting up your Orchestrator or Gateway (formerly called Broadcaster) to utilize the AI Subnet of the [Livepeer protocol](https://livepeer.org/) for AI job processing. -> [!ATTENTION] +> [!CAUTION] > Please note that the AI Subnet is currently in **alpha** and under active development. Running it on the same machine as your main Orchestrator/Gateway node is not recommended due to potential stability issues. Proceed with caution and understand the associated risks. ## Background @@ -69,7 +69,7 @@ Orchestrators on the AI subnet can select the [supported models](#supported-ai-m "warm": true }, { - "pipeline": "text-to-image", + "pipeline": "image-to-image", "model_id": "ByteDance/SDXL-Lightning", "price_per_unit": 4768371 }, @@ -81,6 +81,13 @@ Orchestrators on the AI subnet can select the [supported models](#supported-ai-m ] ``` + In this configuration: + + - `pipeline`: This mandatory field specifies the type of inference you want to run. The currently supported pipelines are `text-to-image`, `image-to-video`, and `image-to-image`. + - `model_id`: This mandatory field is the [Hugging Face model ID](https://huggingface.co/docs/transformers/en/main_classes/model) of the model you want to use. + - `price_per_unit`: This mandatory field is the price in [Wei](https://ethdocs.org/en/latest/ether.html) per unit of work. + - `warm`: This optional field specifies if the model should be kept warm on the GPU. Keeping a model warm on the GPU reduces the time it takes to run the model as the model is already loaded on the GPU. In our current **alpha** phase, we only support one model per GPU. Therefore, if you have one GPU and one model warm, you cannot serve any other models. + 2. **Install Hugging Face CLI**: Install the Hugging Face CLI by running the following command: ```bash @@ -213,9 +220,12 @@ Gateway nodes on the AI subnet can be set up using the [pre-built binaries](http #### AI Job Submission +> [!IMPORTANT] +> If you're using the `warm` flag in your `aiModels.json` ensure you have the right pipeline running on your orchestrator before submitting a job. + To verify the correct functioning of your **off-chain** Gateway and Orchestrator nodes, submit an AI inference job for each of the supported pipelines. -**Text-to-Image Inference Job**: +#### Text-to-Image Inference Job To send an `text-to-image` inference job to the Gateway node and receive the result, follow these steps: @@ -239,7 +249,9 @@ To send an `text-to-image` inference job to the Gateway node and receive the res Congratulations! You've successfully set up your **off-chain** AI Subnet Orchestrator and Gateway nodes to process `text-to-image` inference jobs 🎉. You can repeat the process for the `image-to-video` and `image-to-image` pipelines described below to ensure the correct functioning of all the AI inference pipelines you did setup in your `aiModels.json`. -**Image-to-Image Inference Job**: +#### Image-to-Image Inference Job + + To send an `image-to-image` inference job to the Gateway node and receive the result, follow these steps: @@ -258,26 +270,32 @@ To send an `image-to-image` inference job to the Gateway node and receive the re curl -O 0.0.0.0:8937/stream/dffff04c/6f247287.png ``` -**Image-to-Video Inference Job**: +#### Image-to-Video Inference Job + + To send an `image-to-video` inference job to the Gateway node and receive the result, follow these steps: 1. **Job Submission**: Submit a job using the `curl` command: ```bash - curl -X POST localhost:8936/image-to-video -F image=@ -F model_id=stabilityai/stable-video-diffusion-img2vid-xt-1- + curl -X POST localhost:8937/image-to-video -F image=@ -F model_id=stabilityai/stable-video-diffusion-img2vid-xt-1-1 ``` > [!NOTE] > Substitute `` with the **local path** of the image you want to use for video generation. This command employs [curl](https://curl.se/docs/manpage.html)'s `-F` flag to upload the image file to the Gateway node. Refer to the [curl documentation](https://curl.se/docs/manpage.html) for more details. -2. **Result Retrieval**: After job completion, you'll receive a JSON with a URL to download the result. Use `curl` to download: + The output should look like: ```bash - curl -O 0.0.0.0:8937/stream/dffff04c/6f247287.png + {"images":[{"seed":3865866304,"url":"/stream/8794a01b/1f9bc7f2.mp4"}]} ``` -% TODO: Done till here. +2. **Result Retrieval**: After job completion, you'll receive a JSON with a URL to download the result. Use `curl` to download: + + ```bash + curl -O localhost:8936/stream/8794a01b/1f9bc7f2.mp4 + ``` ## On-chain Setup From a8346e463e046ca596a85d4354c5d97cc2b1e72b Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 28 Mar 2024 13:07:42 +0100 Subject: [PATCH 067/203] docs(ai): add instructions for on-chain configuration of AI Subnet Orchestrator This commit introduces comprehensive instructions for setting up the AI Subnet Orchestrator for on-chain operation. It includes both binary and Docker setup methods. --- doc/ai-subnet.md | 158 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 123 insertions(+), 35 deletions(-) diff --git a/doc/ai-subnet.md b/doc/ai-subnet.md index f93ab7d9d..61b8bc8de 100644 --- a/doc/ai-subnet.md +++ b/doc/ai-subnet.md @@ -3,15 +3,23 @@ Welcome to the [Livepeer Artificial Intelligence (AI) Subnet](https://explorer.livepeer.org/treasury/110409521297538895053642752647313688591695822800862508217133236436856613165807) 🤖! This comprehensive guide will assist you in setting up your Orchestrator or Gateway (formerly called Broadcaster) to utilize the AI Subnet of the [Livepeer protocol](https://livepeer.org/) for AI job processing. > [!CAUTION] -> Please note that the AI Subnet is currently in **alpha** and under active development. Running it on the same machine as your main Orchestrator/Gateway node is not recommended due to potential stability issues. Proceed with caution and understand the associated risks. +> Please note that the _AI Subnet_ is currently in **alpha** and under active development. Running it on the same machine as your main Orchestrator/Gateway node is not recommended due to potential stability issues. Proceed with caution and understand the associated risks. ## Background - + + +## Terminology + +Before proceeding, ensure you understand these key terms: + +- **AI Subnet**: The [client software](https://github.com/livepeer/go-livepeer/tree/ai-video) implementing the AI functionality of the Livepeer protocol that can run AI Orchestration and Gateway nodes both **off-chain** and **on-chain**. +- **Mainnet AI Subnet**: The collective of Orchestrators and Gateways processing AI jobs on the Livepeer mainnet. +- **Mainnet Transcoding Network**: The primary Livepeer network handling video transcoding jobs. ### Supported AI Models -During the **alpha** and **beta** phases, the AI Subnet supports a limited number of AI models per inference pipeline. The currently supported models per pipeline are: +During the **alpha** and **beta** phases, the _AI Subnet_ supports a limited number of AI models per inference pipeline. The currently supported models per pipeline are: **Text-to-Image**: @@ -33,11 +41,11 @@ During the **alpha** and **beta** phases, the AI Subnet supports a limited numbe - [stable-video-diffusion-img2vid-xt](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt) - [stabilityai/stable-video-diffusion-img2vid-xt-1-1](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt-1-1) -When the AI subnet is fully operational we plan to support any ([diffusion](https://huggingface.co/docs/diffusers/en/index)) model that can be run in a docker container. +When the _Mainnet AI Subnet_ is fully operational we plan to support any ([diffusion](https://huggingface.co/docs/diffusers/en/index)) model that can be run in a docker container. ## Prerequisites -Before starting with either the binary or Docker installation for the AI subnet, ensure your system meets these requirements: +Before starting with either the binary or Docker installation for the _Mainnet AI Subnet_, ensure your system meets these requirements: - [An Nvidia GPU](https://developer.nvidia.com/cuda-gpus) - [The Nvidia driver](https://www.nvidia.com/Download/index.aspx) @@ -50,15 +58,15 @@ Before starting with either the binary or Docker installation for the AI subnet, ## Off-chain Setup -For testing and development purposes, it's a good practice to first run the Orchestrator and Gateway nodes **off-chain**. This allows you to quickly test the AI Subnet and ensure that your Orchestrator and Gateway are functioning correctly before connecting them to the on-chain [Livepeer protocol](https://livepeer.org/). +For testing and development purposes, it's a good practice to first run the Orchestrator and Gateway nodes **off-chain**. This allows you to quickly test the _AI Subnet_ and ensure that your Orchestrator and Gateway are functioning correctly before connecting them to the **on-chain** [Livepeer protocol](https://livepeer.org/). ### Orchestrator Setup #### AI Models Configuration -Orchestrators on the AI subnet can select the [supported models](#supported-ai-models) they wish to advertise and process. To do this: +Orchestrators on the _AI Subnet_ can select the [supported models](#supported-ai-models) they wish to advertise and process. To do this: -1. **AI Model Configuration**: Create an `aiModels.json` file in the `~/.lpData` directory to specify the AI models to support in the AI Subnet. Refer to the provided example below for proper formatting: +1. **AI Model Configuration**: Create an `aiModels.json` file in the `~/.lpData` directory to specify the AI models to support in the _AI Subnet_. Refer to the provided example below for proper formatting: ```json [ @@ -97,7 +105,7 @@ Orchestrators on the AI subnet can select the [supported models](#supported-ai-m 3. **Create a Hugging Face Access Token**: Follow the instructions in the [Hugging Face documentation](https://huggingface.co/docs/hub/en/security-tokens) to create a Hugging Face access token and make it available under the `HG_TOKEN` environment variable. This token will download [token-gated models](https://huggingface.co/docs/transformers.js/en/guides/private) from the Hugging Face model hub. Alternatively, you can also install your Hugging Face access token on your machine using [login command](https://huggingface.co/docs/huggingface_hub/en/guides/cli#huggingface-cli-login) of the Hugging Face CLI. > [!IMPORTANT] - > The `ld_checkpoints.sh` script contains the [SVD1.1](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt-1-1) model, which currently also requires you to agree to the model's license agreement. If you want to advertise this model on the AI Subnet, you need to go to the [model page](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt-1-1), log in, and accept their terms. + > The `ld_checkpoints.sh` script contains the [SVD1.1](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt-1-1) model, which currently also requires you to agree to the model's license agreement. If you want to advertise this model on the _AI Subnet_, you need to go to the [model page](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt-1-1), log in, and accept their terms. 4. **Download AI Models**: Download the models listed in `aiModels.json` to the `~/.lpData/models` directory using the [ld_checkpoints.sh](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) script from the [livepeer/ai-worker](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) repository. You can run the following in your terminal to do this: @@ -106,21 +114,21 @@ Orchestrators on the AI subnet can select the [supported models](#supported-ai-m ``` > [!NOTE] - > The `--alpha` flag is used to download only the models currently supported by the Livepeer.inc Gateway node on the AI Subnet. You can remove this flag if you want to download all models and advertise them for other Gateway nodes. + > The `--alpha` flag is used to download only the models currently supported by the Livepeer.inc Gateway node on the _AI Subnet_. You can remove this flag if you want to download all models and advertise them for other Gateway nodes. #### Orchestrator Binary Setup -To run the AI Subnet Orchestrator **off-chain** using the [pre-build binaries](https://discord.com/channels/423160867534929930/577736983036559360), follow these steps: +To run the _AI Subnet_ Orchestrator **off-chain** using the [pre-build binaries](https://discord.com/channels/423160867534929930/577736983036559360), follow these steps: -1. **Download the Latest AI Subnet Binary**: Visit the [#🪛│builds Channel](https://discord.com/channels/423160867534929930/577736983036559360) on the [Livepeer community Discord](https://discord.com/channels/423160867534929930/577736983036559360). Find the latest message mentioning `Branch: ai-video` and your platform under `Platform:`. This message includes the newest AI Subnet binaries for your system. +1. **Download the Latest AI Subnet Binary**: Visit the [#🪛│builds Channel](https://discord.com/channels/423160867534929930/577736983036559360) on the [Livepeer community Discord](https://discord.com/channels/423160867534929930/577736983036559360). Find the latest message mentioning `Branch: ai-video` and your platform under `Platform:`. This message includes the newest _AI Subnet_ binaries for your system. 2. **Extract and Configure the Binary**: Once downloaded, extract the binary to a directory of your choice. -3. **Pull the Latest AI Runner docker image**: The Livepeer AI Subnet uses a containerized workflow to run the AI models. You can download the latest AI Runner container by running the following command: +3. **Pull the Latest AI Runner docker image**: The Livepeer _AI Subnet_ uses a containerized workflow to run the AI models. You can download the latest AI Runner container by running the following command: ```bash docker pull livepeer/ai-runner:latest ``` -4. **Start the Orchestrator**: Run the following command to initiate your AI Subnet Orchestrator: +4. **Start the Orchestrator**: Run the following command to initiate your _AI Subnet_ Orchestrator: ```bash ./livepeer \ @@ -134,9 +142,9 @@ To run the AI Subnet Orchestrator **off-chain** using the [pre-build binaries](h -aiModelsDir ~/.lpData/models ``` - While most of these flags are already used in the [Mainnet transcoding network](https://github.com/livepeer/go-livepeer) (documented in the [Livepeer documentation](https://docs.livepeer.org/references/go-livepeer/cli-reference)), the `-aiWorker`, `-aiModels`, and `-aiModelsDir` flags are new. They enable the AI Subnet Orchestrator, define the location of your AI models configuration, and specify the directory where the models are stored on your machine, respectively. If the `aiModelsDir` flag is not set, the AI Subnet Orchestrator will look for the models in the `~/.lpData//models` directory. + While most of these flags are already used in the [Mainnet transcoding network](https://github.com/livepeer/go-livepeer) (documented in the [Livepeer documentation](https://docs.livepeer.org/references/go-livepeer/cli-reference)), the `-aiWorker`, `-aiModels`, and `-aiModelsDir` flags are new. They enable the _AI Subnet_ Orchestrator, define the location of your AI models configuration, and specify the directory where the models are stored on your machine, respectively. If the `aiModelsDir` flag is not set, the _AI Subnet_ Orchestrator will look for the models in the `~/.lpData//models` directory. -5. **Verify Setup**: Ensure the AI Subnet Orchestrator runs on port `8936`. Open port `8936` on your machine and forward it to the internet for external access. +5. **Verify Setup**: Confirm that the _AI Subnet_ Orchestrator node is operating on port `8936`. To make the Gateway node accessible from the internet, unblock port `8936` on your machine and set up port forwarding on your router. > [!NOTE] > If no binaries are available for your system, you can build the [ai-video branch](https://github.com/livepeer/go-livepeer/tree/ai-video) of [go-livepeer](https://github.com/livepeer/go-livepeer) from source by following the instructions in the [Livepeer repository](https://docs.livepeer.org/orchestrators/guides/install-go-livepeer) or by reaching out to the Livepeer community on [Discord](https://discord.gg/livepeer). @@ -145,21 +153,21 @@ To run the AI Subnet Orchestrator **off-chain** using the [pre-build binaries](h -To run the AI Subnet Orchestrator **off-chain** using Docker, follow these steps: +To run the _AI Subnet_ Orchestrator **off-chain** using Docker, follow these steps: -1. **Pull the AI Subnet Docker Image**: Pull the latest AI subnet Docker image from the [Livepeer Docker Hub](https://hub.docker.com/r/livepeer/go-livepeer-ai) using the following command: +1. **Pull the AI Subnet Docker Image**: Pull the latest _AI Subnet_ Docker image from the [Livepeer Docker Hub](https://hub.docker.com/r/livepeer/go-livepeer-ai) using the following command: ```bash docker pull livepeer/go-livepeer:ai-video ``` -2. **Pull the Latest AI Runner docker image**: The Livepeer AI Subnet uses a [containerized workflow](https://www.ibm.com/topics/containerization) to run the AI models. You can download the latest [AI Runner](https://hub.docker.com/r/livepeer/ai-runner) image by running the following command: +2. **Pull the Latest AI Runner docker image**: The Livepeer _AI Subnet_ uses a [containerized workflow](https://www.ibm.com/topics/containerization) to run the AI models. You can download the latest [AI Runner](https://hub.docker.com/r/livepeer/ai-runner) image by running the following command: ```bash docker pull livepeer/ai-runner:latest ``` -3. **Run the AI Subnet Docker Image**: Execute the following command to start your AI Subnet Orchestrator: +3. **Run the AI Subnet Docker Image**: Execute the following command to start your _AI Subnet_ Orchestrator: ```bash docker run \ @@ -178,18 +186,18 @@ To run the AI Subnet Orchestrator **off-chain** using Docker, follow these steps -aiModelsDir ~/.lpData/models ``` - As outlined in the [Orchestrator Binary Setup](#orchestrator-binary-setup), the `-aiWorker`, `-aiModels`, and `-aiModelsDir` flags are unique to the AI Subnet Orchestrator. The remaining flags are common to the [Mainnet transcoding network](https://github.com/livepeer/go-livepeer) as detailed in the [Livepeer documentation](https://docs.livepeer.org/references/go-livepeer/cli-reference). The AI-specific flags activate the AI Subnet Orchestrator, specify the location of your AI models configuration, and define the directory for model storage on your machine. If `aiModelsDir` is not set, the AI Subnet Orchestrator defaults to the `~/.lpData//models` directory for model storage. + As outlined in the [Orchestrator Binary Setup](#orchestrator-binary-setup), the `-aiWorker`, `-aiModels`, and `-aiModelsDir` flags are unique to the _AI Subnet_ Orchestrator. The remaining flags are common to the [Mainnet transcoding network](https://github.com/livepeer/go-livepeer) as detailed in the [Livepeer documentation](https://docs.livepeer.org/references/go-livepeer/cli-reference). The AI-specific flags activate the _AI Subnet_ Orchestrator, specify the location of your AI models configuration, and define the directory for model storage on your machine. If `aiModelsDir` is not set, the _AI Subnet_ Orchestrator defaults to the `~/.lpData//models` directory for model storage. -4. **Verify Setup**: Ensure that the AI Subnet Orchestrator runs on port `8936`. Open port `8936` on your machine and forward it to the internet for external access. +4. **Verify Setup**: Confirm that the _AI Subnet_ Orchestrator node is operating on port `8936`. To make the Gateway node accessible from the internet, unblock port `8936` on your machine and set up port forwarding on your router. ### Gateway Setup #### Gateway Binary Setup -Gateway nodes on the AI subnet can be set up using the [pre-built binaries](https://discord.com/channels/423160867534929930/577736983036559360). Follow these steps to run the AI Subnet Gateway node **off-chain**: +Gateway nodes on the _AI Subnet_ can be set up using the [pre-built binaries](https://discord.com/channels/423160867534929930/577736983036559360). Follow these steps to run the _AI Subnet_ Gateway node **off-chain**: -1. **Download and extract the latest AI Subnet Binary**: Follow steps 1 and 2 from the [Orchestrator Binary Setup](#orchestrator-binary-setup) to download and extract the latest AI Subnet binary for your system. -2. **Start the Gateway**: Execute the following command to start your AI Subnet Gateway node: +1. **Download and extract the latest AI Subnet Binary**: Follow steps 1 and 2 from the [Orchestrator Binary Setup](#orchestrator-binary-setup) to download and extract the latest _AI Subnet_ binary for your system. +2. **Start the Gateway**: Execute the following command to start your _AI Subnet_ Gateway node: ```bash ./livepeer \ @@ -201,14 +209,14 @@ Gateway nodes on the AI subnet can be set up using the [pre-built binaries](http -httpIngest ``` - The flags used here are also applicable to the [Mainnet transcoding network](https://github.com/livepeer/go-livepeer). For a comprehensive understanding of these flags, consult the [Livepeer documentation](https://docs.livepeer.org/references/go-livepeer/cli-reference). Specifically, the `--orchAddr` and `--httpAddr` flags are crucial for routing the Gateway node to your local Orchestrator (i.e., `0.0.0.0:8936`) and facilitating off-chain communication between the Gateway and the Orchestrator. + The flags used here are also applicable to the [Mainnet transcoding network](https://github.com/livepeer/go-livepeer). For a comprehensive understanding of these flags, consult the [Livepeer documentation](https://docs.livepeer.org/references/go-livepeer/cli-reference). Specifically, the `--orchAddr` and `--httpAddr` flags are crucial for routing the Gateway node to your local Orchestrator (i.e., `0.0.0.0:8936`) and facilitating **off-chain** communication between the Gateway and the Orchestrator. -3. **Verify Setup**: Ensure that the AI Subnet Gateway node runs on port `8937`. Open port `8937` on your machine and forward it to the internet for external access. +3. **Verify Setup**: Confirm that the _AI Subnet_ Gateway node is operating on port `8937`. To make the Gateway node accessible from the internet, unblock port `8937` on your machine and set up port forwarding on your router. #### Gateway Docker Setup -1. **Pull the AI Subnet Docker Image**: Follow step 1 from the [Orchestrator Docker Setup](#orchestrator-docker-setup) to pull the latest AI Subnet Docker image from the [Livepeer Docker Hub](https://hub.docker.com/r/livepeer/go-livepeer-ai). -2. **Run the AI Subnet Docker Image**: Execute the following command to start your AI Subnet Gateway node: +1. **Pull the AI Subnet Docker Image**: Follow step 1 from the [Orchestrator Docker Setup](#orchestrator-docker-setup) to pull the latest _AI Subnet_ Docker image from the [Livepeer Docker Hub](https://hub.docker.com/r/livepeer/go-livepeer-ai). +2. **Run the AI Subnet Docker Image**: Execute the following command to start your _AI Subnet_ Gateway node: ```bash docker run -v ~/.lpData2/:/root/.lpData2 -p 8937:8937 --network host livepeer/go-livepeer:ai-video -datadir ~/.lpData2 -broadcaster -orchAddr -httpAddr 0.0.0.0:8937 -v 6 -httpIngest @@ -216,12 +224,12 @@ Gateway nodes on the AI subnet can be set up using the [pre-built binaries](http As outlined in the [Gateway Binary Setup](#gateway-binary-setup) the flags are common to the [Mainnet transcoding network](https://github.com/livepeer/go-livepeer) and are documented in the [Livepeer documentation](https://docs.livepeer.org/references/go-livepeer/cli-reference). The `--orchAddr` and `--httpAddr` flags are essential for directing the Gateway node to your local Orchestrator and ensuring **off-chain** communication between the Gateway and the Orchestrator, respectively. -3. **Verify Setup**: Ensure that the AI Subnet Gateway node runs on port `8937`. Open port `8937` on your machine and forward it to the internet for external access. +3. **Verify Setup**: Confirm that the _AI Subnet_ Gateway node is operating on port `8937`. To make the Gateway node accessible from the internet, unblock port `8937` on your machine and set up port forwarding on your router. #### AI Job Submission > [!IMPORTANT] -> If you're using the `warm` flag in your `aiModels.json` ensure you have the right pipeline running on your orchestrator before submitting a job. +> If you're using the `warm` flag in your `aiModels.json` ensure you have the right pipeline running on your Orchestrator before submitting a job. To verify the correct functioning of your **off-chain** Gateway and Orchestrator nodes, submit an AI inference job for each of the supported pipelines. @@ -247,7 +255,7 @@ To send an `text-to-image` inference job to the Gateway node and receive the res curl -O 0.0.0.0:8937/stream/34937c31/dc88c7c9.png ``` -Congratulations! You've successfully set up your **off-chain** AI Subnet Orchestrator and Gateway nodes to process `text-to-image` inference jobs 🎉. You can repeat the process for the `image-to-video` and `image-to-image` pipelines described below to ensure the correct functioning of all the AI inference pipelines you did setup in your `aiModels.json`. +Congratulations! You've successfully set up your **off-chain** _AI Subnet_ Orchestrator and Gateway nodes to process `text-to-image` inference jobs. 🎉 You can repeat the process for the `image-to-video` and `image-to-image` pipelines described below to ensure the correct functioning of all the AI inference pipelines you did setup in your `aiModels.json`. #### Image-to-Image Inference Job @@ -297,11 +305,91 @@ To send an `image-to-video` inference job to the Gateway node and receive the re curl -O localhost:8936/stream/8794a01b/1f9bc7f2.mp4 ``` -## On-chain Setup +## On-chain Configuration + +After successful **off-chain** testing of your Orchestrator and Gateway nodes, you can proceed with the **on-chain** configuration. This involves connecting your nodes to the [Livepeer protocol](https://livepeer.org/) for AI job processing on the _AI Subnet_. The instructions below will guide you through the steps to configure your Orchestrator and Gateway nodes **on-chain**, without affecting your main nodes. + +### On-chain AI Subnet Orchestrator Configuration + +To redeem _Mainnet AI Subnet_ tickets **on-chain**, your _Mainnet Transcoding Network_ Orchestrator needs to be set up and ranked within the top 100 Orchestrators. The setup steps are outlined in the [Livepeer Orchestrator Setup Documentation](https://docs.livepeer.org/orchestrators/guides/mainnet-transcoding-network). Once your _Mainnet Transcoding Network_ Orchestrator is set up, you can proceed with the **on-chain** configuration of your _mainnet AI Subnet_ Orchestrator. To do this without affecting your _Mainnet Transcoding Network_ Orchestrator, run a separate Orchestrator exclusively for the _AI Subnet_. This Orchestrator will process AI jobs on the _Mainnet AI Subnet_. If your _Mainnet Transcoding Network_ Orchestrator is up and running, there are two methods to have the AI tickets redeemed **on-chain** by your _Mainnet Transcoding Network_ Orchestrator: + +- **Method 1**: Redeem the AI tickets **on-chain** on your _Mainnet AI Subnet_ Orchestrator, using the `-ethOrchAddr` to set your _Mainnet Transcoding Network_ Orchestrator as the `recipient` of the tickets. **This method is recommended**. +- **Method 2**: Set up a ticket redemption service using the `-redeemer` flag, and have your _Mainnet AI Subnet_ Orchestrator send the tickets to this service using the `redeemerAddr` flag. + +Both methods are detailed below. + +#### Set Ticket Recipient + +The first and **recommended method** is to use the `ethOrchAddr` flag to set the Ethereum address of your _Mainnet Transcoding Network_ Orchestrator as the recipient of the AI tickets. This ensures that the AI tickets are redeemed **on-chain** by your _AI Subnet_ Orchestrator, while the AI rewards are sent to your _Mainnet Transcoding Network_ Orchestrator. Follow these steps to set up your _Mainnet AI Subnet_ Orchestrator using this method: + +1. Create a new Ethereum account for your _Mainnet AI Subnet_ Orchestrator. For security reasons, it's recommended to use a separate account from your _Mainnet Transcoding Network_ Orchestrator. +2. Fund the Ethereum account with enough ETH to cover the gas costs of redeeming the AI tickets **on-chain**. +3. Open port `8936` on your machine and set up port forwarding on your router to make the _Mainnet AI Subnet_ Orchestrator accessible from the internet. +4. Start your _Mainnet AI Subnet_ Orchestrator with the `-ethOrchAddr` flag set to the Ethereum address of your _Mainnet Transcoding Network_ Orchestrator. This flag specifies the recipient of the AI tickets. + + **Binary Command**: + + ```bash + ./livepeer \ + -network arbitrum-one-mainnet \ + -ethUrl https://arb1.arbitrum.io/rpc \ + -orchestrator \ + -transcoder \ + -serviceAddr : \ + -v 6 \ + -nvidia "all" \ + -aiWorker \ + -aiModels ~/.lpData/aiModels.json \ + -aiModelsDir ~/.lpData/models \ + -pricePerUnit 70 \ + -ethAcctAddr \ + -ethOrchAddr + ``` + + **Docker Command**: -#### Put Orchestrator On-chain + ```bash + docker run \ + -v ~/.lpData/:/root/.lpData/ \ + -v /var/run/docker.sock:/var/run/docker.sock \ + --network host \ + --gpus all \ + livepeer/go-livepeer:ai-video \ + -network arbitrum-one-mainnet \ + -ethUrl https://arb1.arbitrum.io/rpc \ + -orchestrator \ + -transcoder \ + -serviceAddr : \ + -v 6 \ + -nvidia "all" \ + -aiWorker \ + -aiModels /root/.lpData/aiModels.json \ + -aiModelsDir ~/.lpData/models \ + -pricePerUnit 70 \ + -ethKeystorePath /root/.lpData/arbitrum-one-mainnet/keystore \ + -ethPassword /root/.lpData/.eth_secret \ + -ethAcctAddr \ + -ethOrchAddr + ``` + + In these configurations: + + - `` is the Ethereum address of your _Mainnet AI Subnet_ Orchestrator. + - `` is the Ethereum address of your _Mainnet Transcoding Network_ Orchestrator, which will receive the AI rewards. + + The other flags are similar to those used in your [main Orchestrator](https://docs.livepeer.org/references/go-livepeer/cli-reference). Note that the `-pricePerUnit` flag, while not utilized by the _Mainnet AI Subnet_ Orchestrator, must be set to satisfy the transcoding client. + +Congratulations! You've successfully configured your _AI Subnet_ Orchestrator to redeem AI tickets **on-chain** and send the rewards to your _Mainnet Transcoding Network_ Orchestrator. 🎉 -There are two ways to put your AI subnet orchestrator **on-chain** to receive jobs. First you can create a new ETH wallet for your AI subnet orchestrator, deposit some eth for gas fees and set the `ethOrchAddr` flag and set it to the Ethereum address of your orchestrator Alternatively, you can run a ticket redemption service on your main orchestrator to redeem tickets on behalf of your AI subnet orchestrator. +#### Use a ticket Redemption Service + +> [!NOTE] +> Coming soon. + +### On-chain Gateway Setup + +> [!IMPORTANT] +> During the **alpha** phase, to streamline our development process, we currently only support the Livepeer.inc Gateway node for **on-chain Gateway** operations. We aim to extend support to other **on-chain** Gateway nodes in the future. Consequently, we do not provide any documentation for setting up an **on-chain** Gateway node at this time. ## Issues From f5bb8c298a0142b94a76bb0b2097a59e0caa0d7c Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 28 Mar 2024 14:01:38 +0100 Subject: [PATCH 068/203] docs(ai): improve AI on-chain instructions This commit restructures the AI on-chain instructions to make them easier to follow. --- doc/ai-subnet.md | 117 +++++++++++++++++++++++++++-------------------- 1 file changed, 68 insertions(+), 49 deletions(-) diff --git a/doc/ai-subnet.md b/doc/ai-subnet.md index 61b8bc8de..1de0d4ab7 100644 --- a/doc/ai-subnet.md +++ b/doc/ai-subnet.md @@ -107,7 +107,7 @@ Orchestrators on the _AI Subnet_ can select the [supported models](#supported-ai > [!IMPORTANT] > The `ld_checkpoints.sh` script contains the [SVD1.1](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt-1-1) model, which currently also requires you to agree to the model's license agreement. If you want to advertise this model on the _AI Subnet_, you need to go to the [model page](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt-1-1), log in, and accept their terms. -4. **Download AI Models**: Download the models listed in `aiModels.json` to the `~/.lpData/models` directory using the [ld_checkpoints.sh](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) script from the [livepeer/ai-worker](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) repository. You can run the following in your terminal to do this: +4. **Download AI Models**: Download the models listed in `aiModels.json` to the `~/.lpData/models` directory using the [ld_checkpoints.sh](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) script from the [livepeer/ai-worker](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) repository. To do this you can run the following in your terminal from the `.lpData` directory: ```bash curl -s https://raw.githubusercontent.com/livepeer/ai-worker/main/runner/dl_checkpoints.sh | bash -s -- --alpha @@ -311,12 +311,12 @@ After successful **off-chain** testing of your Orchestrator and Gateway nodes, y ### On-chain AI Subnet Orchestrator Configuration -To redeem _Mainnet AI Subnet_ tickets **on-chain**, your _Mainnet Transcoding Network_ Orchestrator needs to be set up and ranked within the top 100 Orchestrators. The setup steps are outlined in the [Livepeer Orchestrator Setup Documentation](https://docs.livepeer.org/orchestrators/guides/mainnet-transcoding-network). Once your _Mainnet Transcoding Network_ Orchestrator is set up, you can proceed with the **on-chain** configuration of your _mainnet AI Subnet_ Orchestrator. To do this without affecting your _Mainnet Transcoding Network_ Orchestrator, run a separate Orchestrator exclusively for the _AI Subnet_. This Orchestrator will process AI jobs on the _Mainnet AI Subnet_. If your _Mainnet Transcoding Network_ Orchestrator is up and running, there are two methods to have the AI tickets redeemed **on-chain** by your _Mainnet Transcoding Network_ Orchestrator: +To redeem _Mainnet AI Subnet_ tickets **on-chain**, ensure your _Mainnet Transcoding Network_ Orchestrator is set up and ranked in the top 100. Refer to the [Livepeer Orchestrator Setup Documentation](https://docs.livepeer.org/orchestrators/guides/mainnet-transcoding-network) for setup steps. Once set up, configure your _Mainnet AI Subnet_ Orchestrator **on-chain**. Run a separate Orchestrator for the _AI Subnet_ to avoid affecting your main Orchestrator. This Orchestrator will handle AI jobs on the _Mainnet AI Subnet_. If your main Orchestrator is operational, there are two methods for **on-chain** AI ticket redemption: -- **Method 1**: Redeem the AI tickets **on-chain** on your _Mainnet AI Subnet_ Orchestrator, using the `-ethOrchAddr` to set your _Mainnet Transcoding Network_ Orchestrator as the `recipient` of the tickets. **This method is recommended**. +- **Method 1 (Recommended)**: Redeem the AI tickets **on-chain** on your _Mainnet AI Subnet_ Orchestrator, using the `-ethOrchAddr` to set your _Mainnet Transcoding Network_ Orchestrator as the `recipient` of the tickets. - **Method 2**: Set up a ticket redemption service using the `-redeemer` flag, and have your _Mainnet AI Subnet_ Orchestrator send the tickets to this service using the `redeemerAddr` flag. -Both methods are detailed below. +Detailed instructions for both methods are provided below. #### Set Ticket Recipient @@ -325,61 +325,80 @@ The first and **recommended method** is to use the `ethOrchAddr` flag to set the 1. Create a new Ethereum account for your _Mainnet AI Subnet_ Orchestrator. For security reasons, it's recommended to use a separate account from your _Mainnet Transcoding Network_ Orchestrator. 2. Fund the Ethereum account with enough ETH to cover the gas costs of redeeming the AI tickets **on-chain**. 3. Open port `8936` on your machine and set up port forwarding on your router to make the _Mainnet AI Subnet_ Orchestrator accessible from the internet. -4. Start your _Mainnet AI Subnet_ Orchestrator with the `-ethOrchAddr` flag set to the Ethereum address of your _Mainnet Transcoding Network_ Orchestrator. This flag specifies the recipient of the AI tickets. - **Binary Command**: +After you have completed these steps, you can start your _Mainnet AI Subnet_ Orchestrator using the binary or Docker image. - ```bash - ./livepeer \ - -network arbitrum-one-mainnet \ - -ethUrl https://arb1.arbitrum.io/rpc \ - -orchestrator \ - -transcoder \ - -serviceAddr : \ - -v 6 \ - -nvidia "all" \ - -aiWorker \ - -aiModels ~/.lpData/aiModels.json \ - -aiModelsDir ~/.lpData/models \ - -pricePerUnit 70 \ - -ethAcctAddr \ - -ethOrchAddr - ``` +#### Binary Startup Command + +To start your _Mainnet AI Subnet_ Orchestrator using the [pre-built binaries](https://discord.com/channels/423160867534929930/577736983036559360), use the following command: + +```bash +./livepeer \ + -network arbitrum-one-mainnet \ + -ethUrl https://arb1.arbitrum.io/rpc \ + -orchestrator \ + -transcoder \ + -serviceAddr : \ + -v 6 \ + -nvidia "all" \ + -aiWorker \ + -aiModels ~/.lpData/aiModels.json \ + -aiModelsDir ~/.lpData/models \ + -pricePerUnit 70 \ + -ethAcctAddr \ + -ethOrchAddr +``` + +While most flags found in this command are similar to those used when running [Mainnet transcoding Orchestrator](https://docs.livepeer.org/references/go-livepeer/cli-reference), there are two AI-specific flags to note when setting up your _Mainnet AI Subnet_ Orchestrator **on-chain**: + +- `-ethAcctAddr`: This flag specifies the Ethereum address of your _Mainnet AI Subnet_ Orchestrator. +- `-ethOrchAddr`: This flag specifies the Ethereum address of your _Mainnet Transcoding Network_ Orchestrator. + +Setting these flags correctly ensures that your _Mainnet AI Subnet_ Orchestrator is correctly configured to redeem AI tickets **on-chain**. 🎉 + +#### Docker Startup Command - **Docker Command**: +To start your _Mainnet AI Subnet_ Orchestrator using Docker, follow these steps: + +1. **Mount the AI Subnet Orchestrator Wallet**: Ensure your AI Subnet Orchestrator wallet is available in the `~/.lpData/arbitrum-one-mainnet/keystore` directory. +2. **Mount the AI Subnet Orchestrator Password File**: Ensure your AI Subnet Orchestrator password is available in the `~/.lpData/.eth_secret` file. +3. **Run the Docker Command**: Execute the following command to start your _Mainnet AI Subnet_ Orchestrator: ```bash - docker run \ - -v ~/.lpData/:/root/.lpData/ \ - -v /var/run/docker.sock:/var/run/docker.sock \ - --network host \ - --gpus all \ - livepeer/go-livepeer:ai-video \ - -network arbitrum-one-mainnet \ - -ethUrl https://arb1.arbitrum.io/rpc \ - -orchestrator \ - -transcoder \ - -serviceAddr : \ - -v 6 \ - -nvidia "all" \ - -aiWorker \ - -aiModels /root/.lpData/aiModels.json \ - -aiModelsDir ~/.lpData/models \ - -pricePerUnit 70 \ - -ethKeystorePath /root/.lpData/arbitrum-one-mainnet/keystore \ - -ethPassword /root/.lpData/.eth_secret \ - -ethAcctAddr \ - -ethOrchAddr + docker run \ + -v ~/.lpData/:/root/.lpData/ \ + -v /var/run/docker.sock:/var/run/docker.sock \ + --network host \ + --gpus all \ + livepeer/go-livepeer:ai-video \ + -network arbitrum-one-mainnet \ + -ethUrl https://arb1.arbitrum.io/rpc \ + -orchestrator \ + -transcoder \ + -serviceAddr : \ + -v 6 \ + -nvidia "all" \ + -aiWorker \ + -aiModels /root/.lpData/aiModels.json \ + -aiModelsDir ~/.lpData/models \ + -pricePerUnit 70 \ + -ethKeystorePath /root/.lpData/arbitrum-one-mainnet/keystore \ + -ethPassword /root/.lpData/.eth_secret \ + -ethAcctAddr \ + -ethOrchAddr ``` - In these configurations: +While most flags found in this command are similar to those used when running [Mainnet transcoding Orchestrator](https://docs.livepeer.org/references/go-livepeer/cli-reference), there are four AI-specific flags to note when setting up your Mainnet AI Subnet Orchestrator **on-chain** using docker: + +- `-ethAcctAddr`: This flag specifies the Ethereum address of your _Mainnet AI Subnet_ Orchestrator. +- `-ethOrchAddr`: This flag specifies the Ethereum address of your _Mainnet Transcoding Network_ Orchestrator. - - `` is the Ethereum address of your _Mainnet AI Subnet_ Orchestrator. - - `` is the Ethereum address of your _Mainnet Transcoding Network_ Orchestrator, which will receive the AI rewards. +Additionaly since the _AI Subnet_ software using [Docker-out-of-Docker](http://tdongsi.github.io/blog/2017/04/23/docker-out-of-docker/) to spin up the [AI Runner](https://github.com/livepeer/ai-worker) containers, two additional docker-specific flags are crucial: - The other flags are similar to those used in your [main Orchestrator](https://docs.livepeer.org/references/go-livepeer/cli-reference). Note that the `-pricePerUnit` flag, while not utilized by the _Mainnet AI Subnet_ Orchestrator, must be set to satisfy the transcoding client. +- `--network host`: Enables communication between the Docker daemon inside the container and the [AI Runner](https://github.com/livepeer/ai-worker) containers for AI inference jobs. +- `--aiModelsDir`: Specifies the directory on your **host machine** where AI models are stored. The Docker daemon uses this path to mount the models in the AI Runner containers. -Congratulations! You've successfully configured your _AI Subnet_ Orchestrator to redeem AI tickets **on-chain** and send the rewards to your _Mainnet Transcoding Network_ Orchestrator. 🎉 +Correctly setting these flags configures your _Mainnet AI Subnet_ Orchestrator to redeem AI tickets **on-chain** successfully. 🎉 #### Use a ticket Redemption Service From 16edc6ed0328a58a23738d896f7bfa83af0e3d97 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 28 Mar 2024 15:41:43 +0100 Subject: [PATCH 069/203] docs(ai): improve ai docs syntax This commit applies some small syntax changes to the ai documentation to improve readability. --- doc/ai-subnet.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/doc/ai-subnet.md b/doc/ai-subnet.md index 1de0d4ab7..beebcddad 100644 --- a/doc/ai-subnet.md +++ b/doc/ai-subnet.md @@ -58,7 +58,7 @@ Before starting with either the binary or Docker installation for the _Mainnet A ## Off-chain Setup -For testing and development purposes, it's a good practice to first run the Orchestrator and Gateway nodes **off-chain**. This allows you to quickly test the _AI Subnet_ and ensure that your Orchestrator and Gateway are functioning correctly before connecting them to the **on-chain** [Livepeer protocol](https://livepeer.org/). +For testing and development purposes, it's a good practice first to run the Orchestrator and Gateway nodes **off-chain**. This allows you to quickly test the _AI Subnet_ and ensure that your Orchestrator and Gateway nodes function correctly before connecting them to the **on-chain** [Livepeer protocol](https://livepeer.org/). ### Orchestrator Setup @@ -94,7 +94,7 @@ Orchestrators on the _AI Subnet_ can select the [supported models](#supported-ai - `pipeline`: This mandatory field specifies the type of inference you want to run. The currently supported pipelines are `text-to-image`, `image-to-video`, and `image-to-image`. - `model_id`: This mandatory field is the [Hugging Face model ID](https://huggingface.co/docs/transformers/en/main_classes/model) of the model you want to use. - `price_per_unit`: This mandatory field is the price in [Wei](https://ethdocs.org/en/latest/ether.html) per unit of work. - - `warm`: This optional field specifies if the model should be kept warm on the GPU. Keeping a model warm on the GPU reduces the time it takes to run the model as the model is already loaded on the GPU. In our current **alpha** phase, we only support one model per GPU. Therefore, if you have one GPU and one model warm, you cannot serve any other models. + - `warm`: This optional field specifies if the model should be kept warm on the GPU. Keeping a model warm on the GPU reduces the time it takes to run the model as it is already loaded on the GPU. We only support one model per GPU in our current **alpha** phase. Therefore, if you have one GPU and one model warm, you cannot serve any other models. 2. **Install Hugging Face CLI**: Install the Hugging Face CLI by running the following command: @@ -186,7 +186,7 @@ To run the _AI Subnet_ Orchestrator **off-chain** using Docker, follow these ste -aiModelsDir ~/.lpData/models ``` - As outlined in the [Orchestrator Binary Setup](#orchestrator-binary-setup), the `-aiWorker`, `-aiModels`, and `-aiModelsDir` flags are unique to the _AI Subnet_ Orchestrator. The remaining flags are common to the [Mainnet transcoding network](https://github.com/livepeer/go-livepeer) as detailed in the [Livepeer documentation](https://docs.livepeer.org/references/go-livepeer/cli-reference). The AI-specific flags activate the _AI Subnet_ Orchestrator, specify the location of your AI models configuration, and define the directory for model storage on your machine. If `aiModelsDir` is not set, the _AI Subnet_ Orchestrator defaults to the `~/.lpData//models` directory for model storage. + As outlined in the [Orchestrator Binary Setup](#orchestrator-binary-setup), the `-aiWorker`, `-aiModels`, and `-aiModelsDir` flags are unique to the _AI Subnet_ Orchestrator. The remaining flags are common to the [Mainnet transcoding network](https://github.com/livepeer/go-livepeer) as detailed in the [Livepeer documentation](https://docs.livepeer.org/references/go-livepeer/cli-reference). The AI-specific flags activate the _AI Subnet_ Orchestrator, specify the location of your AI models configuration file, and define the directory for model storage on your machine. If `aiModelsDir` is not set, the _AI Subnet_ Orchestrator defaults to the `~/.lpData//models` directory for model storage. 4. **Verify Setup**: Confirm that the _AI Subnet_ Orchestrator node is operating on port `8936`. To make the Gateway node accessible from the internet, unblock port `8936` on your machine and set up port forwarding on your router. @@ -209,7 +209,7 @@ Gateway nodes on the _AI Subnet_ can be set up using the [pre-built binaries](ht -httpIngest ``` - The flags used here are also applicable to the [Mainnet transcoding network](https://github.com/livepeer/go-livepeer). For a comprehensive understanding of these flags, consult the [Livepeer documentation](https://docs.livepeer.org/references/go-livepeer/cli-reference). Specifically, the `--orchAddr` and `--httpAddr` flags are crucial for routing the Gateway node to your local Orchestrator (i.e., `0.0.0.0:8936`) and facilitating **off-chain** communication between the Gateway and the Orchestrator. + The flags used here also apply to the [Mainnet transcoding network](https://github.com/livepeer/go-livepeer). To comprehensively understand these flags, consult the [Livepeer documentation](https://docs.livepeer.org/references/go-livepeer/cli-reference). Specifically, the `--orchAddr` and `--httpAddr` flags are crucial for routing the Gateway node to your local Orchestrator (i.e., `0.0.0.0:8936`) and facilitating **off-chain** communication between the Gateway and the Orchestrator. 3. **Verify Setup**: Confirm that the _AI Subnet_ Gateway node is operating on port `8937`. To make the Gateway node accessible from the internet, unblock port `8937` on your machine and set up port forwarding on your router. @@ -222,7 +222,7 @@ Gateway nodes on the _AI Subnet_ can be set up using the [pre-built binaries](ht docker run -v ~/.lpData2/:/root/.lpData2 -p 8937:8937 --network host livepeer/go-livepeer:ai-video -datadir ~/.lpData2 -broadcaster -orchAddr -httpAddr 0.0.0.0:8937 -v 6 -httpIngest ``` - As outlined in the [Gateway Binary Setup](#gateway-binary-setup) the flags are common to the [Mainnet transcoding network](https://github.com/livepeer/go-livepeer) and are documented in the [Livepeer documentation](https://docs.livepeer.org/references/go-livepeer/cli-reference). The `--orchAddr` and `--httpAddr` flags are essential for directing the Gateway node to your local Orchestrator and ensuring **off-chain** communication between the Gateway and the Orchestrator, respectively. + As outlined in the [Gateway Binary Setup](#gateway-binary-setup), the flags are common to the [Mainnet transcoding network](https://github.com/livepeer/go-livepeer) and are documented in the [Livepeer documentation](https://docs.livepeer.org/references/go-livepeer/cli-reference). The `--orchAddr` and `--httpAddr` flags are essential for directing the Gateway node to your local Orchestrator and ensuring **off-chain** communication between the Gateway and the Orchestrator, respectively. 3. **Verify Setup**: Confirm that the _AI Subnet_ Gateway node is operating on port `8937`. To make the Gateway node accessible from the internet, unblock port `8937` on your machine and set up port forwarding on your router. @@ -231,11 +231,11 @@ Gateway nodes on the _AI Subnet_ can be set up using the [pre-built binaries](ht > [!IMPORTANT] > If you're using the `warm` flag in your `aiModels.json` ensure you have the right pipeline running on your Orchestrator before submitting a job. -To verify the correct functioning of your **off-chain** Gateway and Orchestrator nodes, submit an AI inference job for each of the supported pipelines. +Submit an AI inference job for each of the supported pipelines to verify the correct functioning of your **off-chain** Gateway and Orchestrator nodes. #### Text-to-Image Inference Job -To send an `text-to-image` inference job to the Gateway node and receive the result, follow these steps: +To send a `text-to-image` inference job to the Gateway node and receive the result, follow these steps: 1. **Job Submission**: Submit a job using the `curl` command: @@ -255,7 +255,7 @@ To send an `text-to-image` inference job to the Gateway node and receive the res curl -O 0.0.0.0:8937/stream/34937c31/dc88c7c9.png ``` -Congratulations! You've successfully set up your **off-chain** _AI Subnet_ Orchestrator and Gateway nodes to process `text-to-image` inference jobs. 🎉 You can repeat the process for the `image-to-video` and `image-to-image` pipelines described below to ensure the correct functioning of all the AI inference pipelines you did setup in your `aiModels.json`. +Congratulations! You've successfully set up your **off-chain** _AI Subnet_ Orchestrator and Gateway nodes to process `text-to-image` inference jobs. 🎉 You can repeat the process for the `image-to-video` and `image-to-image` pipelines described below to ensure the correct functioning of all the AI inference pipelines you did set up in your `aiModels.json`. #### Image-to-Image Inference Job @@ -313,7 +313,7 @@ After successful **off-chain** testing of your Orchestrator and Gateway nodes, y To redeem _Mainnet AI Subnet_ tickets **on-chain**, ensure your _Mainnet Transcoding Network_ Orchestrator is set up and ranked in the top 100. Refer to the [Livepeer Orchestrator Setup Documentation](https://docs.livepeer.org/orchestrators/guides/mainnet-transcoding-network) for setup steps. Once set up, configure your _Mainnet AI Subnet_ Orchestrator **on-chain**. Run a separate Orchestrator for the _AI Subnet_ to avoid affecting your main Orchestrator. This Orchestrator will handle AI jobs on the _Mainnet AI Subnet_. If your main Orchestrator is operational, there are two methods for **on-chain** AI ticket redemption: -- **Method 1 (Recommended)**: Redeem the AI tickets **on-chain** on your _Mainnet AI Subnet_ Orchestrator, using the `-ethOrchAddr` to set your _Mainnet Transcoding Network_ Orchestrator as the `recipient` of the tickets. +- **Method 1 (Recommended)**: Redeem the AI tickets **on-chain** on your _Mainnet AI Subnet_ Orchestrator, using the `-ethOrchAddr` to set your _Mainnet Transcoding Network_ Orchestrator as the tickets `recipient. - **Method 2**: Set up a ticket redemption service using the `-redeemer` flag, and have your _Mainnet AI Subnet_ Orchestrator send the tickets to this service using the `redeemerAddr` flag. Detailed instructions for both methods are provided below. @@ -322,7 +322,7 @@ Detailed instructions for both methods are provided below. The first and **recommended method** is to use the `ethOrchAddr` flag to set the Ethereum address of your _Mainnet Transcoding Network_ Orchestrator as the recipient of the AI tickets. This ensures that the AI tickets are redeemed **on-chain** by your _AI Subnet_ Orchestrator, while the AI rewards are sent to your _Mainnet Transcoding Network_ Orchestrator. Follow these steps to set up your _Mainnet AI Subnet_ Orchestrator using this method: -1. Create a new Ethereum account for your _Mainnet AI Subnet_ Orchestrator. For security reasons, it's recommended to use a separate account from your _Mainnet Transcoding Network_ Orchestrator. +1. Create a new Ethereum account for your _Mainnet AI Subnet_ Orchestrator. It is recommended that you use a separate account from your _Mainnet Transcoding Network_ Orchestrator for security reasons. 2. Fund the Ethereum account with enough ETH to cover the gas costs of redeeming the AI tickets **on-chain**. 3. Open port `8936` on your machine and set up port forwarding on your router to make the _Mainnet AI Subnet_ Orchestrator accessible from the internet. @@ -354,7 +354,7 @@ While most flags found in this command are similar to those used when running [M - `-ethAcctAddr`: This flag specifies the Ethereum address of your _Mainnet AI Subnet_ Orchestrator. - `-ethOrchAddr`: This flag specifies the Ethereum address of your _Mainnet Transcoding Network_ Orchestrator. -Setting these flags correctly ensures that your _Mainnet AI Subnet_ Orchestrator is correctly configured to redeem AI tickets **on-chain**. 🎉 +Setting these flags ensures that your _Mainnet AI Subnet_ Orchestrator is correctly configured to redeem AI tickets **on-chain**. 🎉 #### Docker Startup Command @@ -388,7 +388,7 @@ To start your _Mainnet AI Subnet_ Orchestrator using Docker, follow these steps: -ethOrchAddr ``` -While most flags found in this command are similar to those used when running [Mainnet transcoding Orchestrator](https://docs.livepeer.org/references/go-livepeer/cli-reference), there are four AI-specific flags to note when setting up your Mainnet AI Subnet Orchestrator **on-chain** using docker: +While most flags found in this command are similar to those used when running [Mainnet transcoding Orchestrator](https://docs.livepeer.org/references/go-livepeer/cli-reference), there are four AI-specific flags to note when setting up your Mainnet AI Subnet Orchestrator **on-chain** using Docker: - `-ethAcctAddr`: This flag specifies the Ethereum address of your _Mainnet AI Subnet_ Orchestrator. - `-ethOrchAddr`: This flag specifies the Ethereum address of your _Mainnet Transcoding Network_ Orchestrator. @@ -398,7 +398,7 @@ Additionaly since the _AI Subnet_ software using [Docker-out-of-Docker](http://t - `--network host`: Enables communication between the Docker daemon inside the container and the [AI Runner](https://github.com/livepeer/ai-worker) containers for AI inference jobs. - `--aiModelsDir`: Specifies the directory on your **host machine** where AI models are stored. The Docker daemon uses this path to mount the models in the AI Runner containers. -Correctly setting these flags configures your _Mainnet AI Subnet_ Orchestrator to redeem AI tickets **on-chain** successfully. 🎉 +Correctly setting these flags configures your _Mainnet AI Subnet_ Orchestrator to successfully redeem AI tickets **on-chain**. 🎉 #### Use a ticket Redemption Service @@ -408,7 +408,7 @@ Correctly setting these flags configures your _Mainnet AI Subnet_ Orchestrator t ### On-chain Gateway Setup > [!IMPORTANT] -> During the **alpha** phase, to streamline our development process, we currently only support the Livepeer.inc Gateway node for **on-chain Gateway** operations. We aim to extend support to other **on-chain** Gateway nodes in the future. Consequently, we do not provide any documentation for setting up an **on-chain** Gateway node at this time. +> During the **alpha** phase, we're focusing our development efforts on the Livepeer.inc Gateway node for **on-chain** operations. While we plan to support additional **on-chain** Gateway nodes in the future, we currently don't offer setup documentation for them. ## Issues From 07ec545e6cea7316dcf98cf332567595f684bd84 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 28 Mar 2024 15:25:06 +0100 Subject: [PATCH 070/203] docs(ai): add promtail metrics sending docs This commit adds some documentation on how people can **opt-in** to send us data about their AI Subnet Orchestrator. --- doc/ai-subnet.md | 62 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/doc/ai-subnet.md b/doc/ai-subnet.md index beebcddad..a2fd3f2c6 100644 --- a/doc/ai-subnet.md +++ b/doc/ai-subnet.md @@ -410,6 +410,68 @@ Correctly setting these flags configures your _Mainnet AI Subnet_ Orchestrator t > [!IMPORTANT] > During the **alpha** phase, we're focusing our development efforts on the Livepeer.inc Gateway node for **on-chain** operations. While we plan to support additional **on-chain** Gateway nodes in the future, we currently don't offer setup documentation for them. +## Provide Metrics to the AI SPE Team + +### Orchestrator Metrics + +> [!IMPORTANT] +> Currently, only _AI Subnet_ Orchestrator nodes running inside a Docker container can send metrics. Binary installation does not support this feature. + +To help the AI SPE team monitor the _AI Subnet_ performance, you can opt to send metrics from your _AI Subnet_ Orchestrator. These metrics aid in understanding network health and identifying potential issues. Follow these steps: + +1. **Install Promtail**: Install [Promtail](https://grafana.com/docs/loki/latest/send-data/promtail/installation/) on your Orchestrator node: + + ```bash + docker pull grafana/promtail:2.9.4 + ``` + +2. **Configure Promtail**: Create a Promtail folder at `/etc/promtail` and add a `ai_subnet_promtail.yml` configuration file in this directory: + + ```yaml + # Replace placeholders with your details + clients: + - url: https://YOUR_NAME_HERE:YOUR_PASSWORD_HERE@dca-loki.livepeer.fun/loki/api/v1/push + external_labels: + node_name: YOUR_SERVER_NAME_HERE + operator: YOUR_NAME_HERE + + scrape_configs: + - job_name: docker_logs + docker_sd_configs: + - host: unix:///var/run/docker.sock + refresh_interval: 5s + relabel_configs: + - source_labels: ["__meta_docker_container_name"] + regex: "/(.*)" + target_label: "container" + - source_labels: ["__meta_docker_container_log_stream"] + target_label: "logstream" + - source_labels: ["__meta_docker_container_label_logging_jobname"] + target_label: "job" + ``` + +3. **Name Your Server**: Replace `YOUR_SERVER_NAME_HERE` with a unique server name. +4. **Obtain Credentials**: Contact the AI SPE team on the [ai-video channel](https://discord.com/channels/423160867534929930/1187806216185974934) of the Livepeer community on [Discord](https://discord.gg/livepeer) for `YOUR_NAME_HERE`, `YOUR_PASSWORD`, and `YOUR_SERVER_NAME_HERE` values. Update these in the Promtail configuration file. +5. **Start Promtail**: Run the following command: + + ```bash + docker run \ + -d \ + --name=ai_subnet_promtail \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v /etc/promtail/ai_subnet_promtail.yml:/etc/promtail/ai_subnet_promtail.yml \ + --network=host \ + grafana/promtail:2.9.4 \ + -config.file=/etc/promtail/ai_subnet_promtail.yml + ``` + +Thank you for contributing to the AI SPE team's efforts to optimize the _AI Subnet_! 🚀 + +### Gateway Metrics + +> [!IMPORTANT] +> During the alpha phase, we only support the Livepeer.inc Gateway node for on-chain Gateway operations to streamline our development process. We plan to extend support to other on-chain Gateway nodes in the future. Therefore, we currently do not provide documentation for sending Gateway node metrics. + ## Issues If you encounter any issues or have questions, feel free to reach out to us in the [ai-video channel](https://discord.com/channels/423160867534929930/1187806216185974934) of the Livepeer community on [Discord](https://discord.gg/livepeer). From bced6be2e1e5ac9267266c6d9620bb9cbb9e7a97 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 28 Mar 2024 19:07:56 +0100 Subject: [PATCH 071/203] docs(ai): update volume mount config for promtail This update modifies the volume mount for the promptail docker container to remove the ai_subnet_promtail.yml file, addressing issues reported by several users with the previous promtail docker configuration. --- doc/ai-subnet.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ai-subnet.md b/doc/ai-subnet.md index a2fd3f2c6..462058e93 100644 --- a/doc/ai-subnet.md +++ b/doc/ai-subnet.md @@ -459,7 +459,7 @@ To help the AI SPE team monitor the _AI Subnet_ performance, you can opt to send -d \ --name=ai_subnet_promtail \ -v /var/run/docker.sock:/var/run/docker.sock \ - -v /etc/promtail/ai_subnet_promtail.yml:/etc/promtail/ai_subnet_promtail.yml \ + -v /etc/promtail/:/etc/promtail/ \ --network=host \ grafana/promtail:2.9.4 \ -config.file=/etc/promtail/ai_subnet_promtail.yml From 5e26f41c2fde99704f868f3182a134f0b83fafb1 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Fri, 29 Mar 2024 21:30:27 +0100 Subject: [PATCH 072/203] docs(ai): name docker containers This commit ensures that people use named docker containers. This will help us monitor their metrics when they decide to share them. --- doc/ai-subnet.md | 57 +++++++++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/doc/ai-subnet.md b/doc/ai-subnet.md index beebcddad..cd253adf9 100644 --- a/doc/ai-subnet.md +++ b/doc/ai-subnet.md @@ -171,6 +171,7 @@ To run the _AI Subnet_ Orchestrator **off-chain** using Docker, follow these ste ```bash docker run \ + --name livepeer_ai_orchestrator \ -v ~/.lpData/:/root/.lpData/ \ -v /var/run/docker.sock:/var/run/docker.sock \ --network host \ @@ -219,7 +220,18 @@ Gateway nodes on the _AI Subnet_ can be set up using the [pre-built binaries](ht 2. **Run the AI Subnet Docker Image**: Execute the following command to start your _AI Subnet_ Gateway node: ```bash - docker run -v ~/.lpData2/:/root/.lpData2 -p 8937:8937 --network host livepeer/go-livepeer:ai-video -datadir ~/.lpData2 -broadcaster -orchAddr -httpAddr 0.0.0.0:8937 -v 6 -httpIngest + docker run \ + --name livepeer_ai_gateway \ + -v ~/.lpData2/:/root/.lpData2 \ + -p 8937:8937 \ + --network host \ + livepeer/go-livepeer:ai-video \ + -datadir ~/.lpData2 \ + -broadcaster \ + -orchAddr \ + -httpAddr 0.0.0.0:8937 \ + -v 6 \ + -httpIngest ``` As outlined in the [Gateway Binary Setup](#gateway-binary-setup), the flags are common to the [Mainnet transcoding network](https://github.com/livepeer/go-livepeer) and are documented in the [Livepeer documentation](https://docs.livepeer.org/references/go-livepeer/cli-reference). The `--orchAddr` and `--httpAddr` flags are essential for directing the Gateway node to your local Orchestrator and ensuring **off-chain** communication between the Gateway and the Orchestrator, respectively. @@ -365,27 +377,28 @@ To start your _Mainnet AI Subnet_ Orchestrator using Docker, follow these steps: 3. **Run the Docker Command**: Execute the following command to start your _Mainnet AI Subnet_ Orchestrator: ```bash - docker run \ - -v ~/.lpData/:/root/.lpData/ \ - -v /var/run/docker.sock:/var/run/docker.sock \ - --network host \ - --gpus all \ - livepeer/go-livepeer:ai-video \ - -network arbitrum-one-mainnet \ - -ethUrl https://arb1.arbitrum.io/rpc \ - -orchestrator \ - -transcoder \ - -serviceAddr : \ - -v 6 \ - -nvidia "all" \ - -aiWorker \ - -aiModels /root/.lpData/aiModels.json \ - -aiModelsDir ~/.lpData/models \ - -pricePerUnit 70 \ - -ethKeystorePath /root/.lpData/arbitrum-one-mainnet/keystore \ - -ethPassword /root/.lpData/.eth_secret \ - -ethAcctAddr \ - -ethOrchAddr + docker run \ + --name livepeer_ai_orchestrator \ + -v ~/.lpData/:/root/.lpData/ \ + -v /var/run/docker.sock:/var/run/docker.sock \ + --network host \ + --gpus all \ + livepeer/go-livepeer:ai-video \ + -network arbitrum-one-mainnet \ + -ethUrl https://arb1.arbitrum.io/rpc \ + -orchestrator \ + -transcoder \ + -serviceAddr : \ + -v 6 \ + -nvidia "all" \ + -aiWorker \ + -aiModels /root/.lpData/aiModels.json \ + -aiModelsDir ~/.lpData/models \ + -pricePerUnit 70 \ + -ethKeystorePath /root/.lpData/arbitrum-one-mainnet/keystore \ + -ethPassword /root/.lpData/.eth_secret \ + -ethAcctAddr \ + -ethOrchAddr ``` While most flags found in this command are similar to those used when running [Mainnet transcoding Orchestrator](https://docs.livepeer.org/references/go-livepeer/cli-reference), there are four AI-specific flags to note when setting up your Mainnet AI Subnet Orchestrator **on-chain** using Docker: From ab40d04619768c4dc940b6bdef40c65371ba63ac Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Fri, 5 Apr 2024 21:32:52 +0200 Subject: [PATCH 073/203] docs(ai): improve models config descriptiona and add `ticketEV` param This commit improves the description of the `warm` configuration value and add the `ticketEV` flag to the Livepeer command line arguments to prevent the `too many values` error from occuring. This error is thrown because the current software does not allow sending more than 150 tickets in one batch. --- doc/ai-subnet.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/ai-subnet.md b/doc/ai-subnet.md index e6afd812a..df61f4435 100644 --- a/doc/ai-subnet.md +++ b/doc/ai-subnet.md @@ -94,7 +94,8 @@ Orchestrators on the _AI Subnet_ can select the [supported models](#supported-ai - `pipeline`: This mandatory field specifies the type of inference you want to run. The currently supported pipelines are `text-to-image`, `image-to-video`, and `image-to-image`. - `model_id`: This mandatory field is the [Hugging Face model ID](https://huggingface.co/docs/transformers/en/main_classes/model) of the model you want to use. - `price_per_unit`: This mandatory field is the price in [Wei](https://ethdocs.org/en/latest/ether.html) per unit of work. - - `warm`: This optional field specifies if the model should be kept warm on the GPU. Keeping a model warm on the GPU reduces the time it takes to run the model as it is already loaded on the GPU. We only support one model per GPU in our current **alpha** phase. Therefore, if you have one GPU and one model warm, you cannot serve any other models. + - `warm`: By default, the Livepeer software dynamically loads the model onto the GPU as needed. However, if you set this flag to `true`, the model will be preloaded onto the GPU when the Orchestrator starts and will remain there, a state referred to as 'warm'. This approach reduces the model's runtime as it's already loaded onto the GPU when requests com in. Please note that in our current **alpha** phase, we only support one model per GPU. Consequently, if you have one GPU and one 'warm' model, you won't be able to serve any other models. + - `warm`: By default, the Livepeer software loads the AI model onto the GPU on-demand. If you set this flag to `true`, the model is preloaded onto the GPU when the Orchestrator starts, keeping it 'warm'. This means it stays loaded on the GPU, reducing the model's execution time as it's immediately available when requests come in. However, in our current **alpha** phase, we support only one model per GPU. So, if you have a single GPU and a 'warm' model, you won't be able to serve additional models. 2. **Install Hugging Face CLI**: Install the Hugging Face CLI by running the following command: @@ -357,6 +358,7 @@ To start your _Mainnet AI Subnet_ Orchestrator using the [pre-built binaries](ht -aiModels ~/.lpData/aiModels.json \ -aiModelsDir ~/.lpData/models \ -pricePerUnit 70 \ + -ticketEV 2999999999999 \ -ethAcctAddr \ -ethOrchAddr ``` @@ -395,6 +397,7 @@ To start your _Mainnet AI Subnet_ Orchestrator using Docker, follow these steps: -aiModels /root/.lpData/aiModels.json \ -aiModelsDir ~/.lpData/models \ -pricePerUnit 70 \ + -ticketEV 2999999999999 \ -ethKeystorePath /root/.lpData/arbitrum-one-mainnet/keystore \ -ethPassword /root/.lpData/.eth_secret \ -ethAcctAddr \ @@ -406,7 +409,7 @@ While most flags found in this command are similar to those used when running [M - `-ethAcctAddr`: This flag specifies the Ethereum address of your _Mainnet AI Subnet_ Orchestrator. - `-ethOrchAddr`: This flag specifies the Ethereum address of your _Mainnet Transcoding Network_ Orchestrator. -Additionaly since the _AI Subnet_ software using [Docker-out-of-Docker](http://tdongsi.github.io/blog/2017/04/23/docker-out-of-docker/) to spin up the [AI Runner](https://github.com/livepeer/ai-worker) containers, two additional docker-specific flags are crucial: +Additionally since the _AI Subnet_ software using [Docker-out-of-Docker](http://tdongsi.github.io/blog/2017/04/23/docker-out-of-docker/) to spin up the [AI Runner](https://github.com/livepeer/ai-worker) containers, two additional docker-specific flags are crucial: - `--network host`: Enables communication between the Docker daemon inside the container and the [AI Runner](https://github.com/livepeer/ai-worker) containers for AI inference jobs. - `--aiModelsDir`: Specifies the directory on your **host machine** where AI models are stored. The Docker daemon uses this path to mount the models in the AI Runner containers. From aa706dcab229265a3f4ad7ce0e3ce1a5e30919b0 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Fri, 5 Apr 2024 21:53:16 +0200 Subject: [PATCH 074/203] docs(ai): improve cli description and remove redeemer method This commit ensures it is clear that the redeemer is not recommended when setting up a AI subnet orchestrator. It also explains that the `pricePerUnit` flag is not needed for the AI subnet orchestrator. --- doc/ai-subnet.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/ai-subnet.md b/doc/ai-subnet.md index df61f4435..e722a1d71 100644 --- a/doc/ai-subnet.md +++ b/doc/ai-subnet.md @@ -329,7 +329,7 @@ To redeem _Mainnet AI Subnet_ tickets **on-chain**, ensure your _Mainnet Transco - **Method 1 (Recommended)**: Redeem the AI tickets **on-chain** on your _Mainnet AI Subnet_ Orchestrator, using the `-ethOrchAddr` to set your _Mainnet Transcoding Network_ Orchestrator as the tickets `recipient. - **Method 2**: Set up a ticket redemption service using the `-redeemer` flag, and have your _Mainnet AI Subnet_ Orchestrator send the tickets to this service using the `redeemerAddr` flag. -Detailed instructions for both methods are provided below. +Below, you'll find comprehensive instructions for the first method. While a [second method is available](https://docs.livepeer.org/references/go-livepeer/cli-reference#onchain), it's generally not advised due to its requirement for configuring a ticket redemption service directly on your primary Orchestrator. This setup could impact the Orchestrator's performance, introducing extra latency and the possibility of other complications. #### Set Ticket Recipient @@ -363,7 +363,7 @@ To start your _Mainnet AI Subnet_ Orchestrator using the [pre-built binaries](ht -ethOrchAddr ``` -While most flags found in this command are similar to those used when running [Mainnet transcoding Orchestrator](https://docs.livepeer.org/references/go-livepeer/cli-reference), there are two AI-specific flags to note when setting up your _Mainnet AI Subnet_ Orchestrator **on-chain**: +The `pricePerUnit` flag is disregarded by the _AI Subnet_ Orchestrator, as the pricing is determined by the [AI models configuration file](#ai-models-configuration). However, it's currently still necessary to initiate the Orchestrator. While the majority of the other flags in this command resemble those used when operating a [Mainnet transcoding Orchestrator](https://docs.livepeer.org/references/go-livepeer/cli-reference), there are two AI-specific flags to be aware of when setting up your _Mainnet AI Subnet_ Orchestrator **on-chain** using Docker: - `-ethAcctAddr`: This flag specifies the Ethereum address of your _Mainnet AI Subnet_ Orchestrator. - `-ethOrchAddr`: This flag specifies the Ethereum address of your _Mainnet Transcoding Network_ Orchestrator. @@ -404,7 +404,7 @@ To start your _Mainnet AI Subnet_ Orchestrator using Docker, follow these steps: -ethOrchAddr ``` -While most flags found in this command are similar to those used when running [Mainnet transcoding Orchestrator](https://docs.livepeer.org/references/go-livepeer/cli-reference), there are four AI-specific flags to note when setting up your Mainnet AI Subnet Orchestrator **on-chain** using Docker: +The `pricePerUnit` flag is disregarded by the _AI Subnet_ Orchestrator, as the pricing is determined by the [AI models configuration file](#ai-models-configuration). However, it's currently still necessary to initiate the Orchestrator. While the majority of the other flags in this command resemble those used when operating a [Mainnet transcoding Orchestrator](https://docs.livepeer.org/references/go-livepeer/cli-reference), there are four AI-specific flags to be aware of when setting up your _Mainnet AI Subnet_ Orchestrator **on-chain** using Docker: - `-ethAcctAddr`: This flag specifies the Ethereum address of your _Mainnet AI Subnet_ Orchestrator. - `-ethOrchAddr`: This flag specifies the Ethereum address of your _Mainnet Transcoding Network_ Orchestrator. From 0e6be96cb85fd3ae7ab01dc52d611a83a2672595 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Fri, 5 Apr 2024 22:23:55 +0200 Subject: [PATCH 075/203] docs(ai): add command outputs This commit ensures that users know which output to expect when they run the commands in the documentation: --- doc/ai-subnet.md | 58 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/doc/ai-subnet.md b/doc/ai-subnet.md index e722a1d71..267e2efc5 100644 --- a/doc/ai-subnet.md +++ b/doc/ai-subnet.md @@ -145,7 +145,14 @@ To run the _AI Subnet_ Orchestrator **off-chain** using the [pre-build binaries] While most of these flags are already used in the [Mainnet transcoding network](https://github.com/livepeer/go-livepeer) (documented in the [Livepeer documentation](https://docs.livepeer.org/references/go-livepeer/cli-reference)), the `-aiWorker`, `-aiModels`, and `-aiModelsDir` flags are new. They enable the _AI Subnet_ Orchestrator, define the location of your AI models configuration, and specify the directory where the models are stored on your machine, respectively. If the `aiModelsDir` flag is not set, the _AI Subnet_ Orchestrator will look for the models in the `~/.lpData//models` directory. -5. **Verify Setup**: Confirm that the _AI Subnet_ Orchestrator node is operating on port `8936`. To make the Gateway node accessible from the internet, unblock port `8936` on your machine and set up port forwarding on your router. +5. **Verify Setup**: If your _AI Subnet_ Orchestrator is running correctly, you should see the following output: + + ```bash + I0405 22:03:17.427058 2655655 rpc.go:301] Connecting RPC to uri=https://0.0.0.0:8936 + I0405 22:03:17.430371 2655655 rpc.go:254] Received Ping request + ``` + +6. **Check Port Availability**: Confirm that the _AI Subnet_ Orchestrator node is operating on port `8936`. Unblock port `8936` on your machine and set up port forwarding on your router to make the Gateway node accessible from the internet. > [!NOTE] > If no binaries are available for your system, you can build the [ai-video branch](https://github.com/livepeer/go-livepeer/tree/ai-video) of [go-livepeer](https://github.com/livepeer/go-livepeer) from source by following the instructions in the [Livepeer repository](https://docs.livepeer.org/orchestrators/guides/install-go-livepeer) or by reaching out to the Livepeer community on [Discord](https://discord.gg/livepeer). @@ -190,7 +197,14 @@ To run the _AI Subnet_ Orchestrator **off-chain** using Docker, follow these ste As outlined in the [Orchestrator Binary Setup](#orchestrator-binary-setup), the `-aiWorker`, `-aiModels`, and `-aiModelsDir` flags are unique to the _AI Subnet_ Orchestrator. The remaining flags are common to the [Mainnet transcoding network](https://github.com/livepeer/go-livepeer) as detailed in the [Livepeer documentation](https://docs.livepeer.org/references/go-livepeer/cli-reference). The AI-specific flags activate the _AI Subnet_ Orchestrator, specify the location of your AI models configuration file, and define the directory for model storage on your machine. If `aiModelsDir` is not set, the _AI Subnet_ Orchestrator defaults to the `~/.lpData//models` directory for model storage. -4. **Verify Setup**: Confirm that the _AI Subnet_ Orchestrator node is operating on port `8936`. To make the Gateway node accessible from the internet, unblock port `8936` on your machine and set up port forwarding on your router. +4. **Verify Setup**: If your _AI Subnet_ Orchestrator is running correctly, you should see the following output: + + ```bash + I0405 22:03:17.427058 2655655 rpc.go:301] Connecting RPC to uri=https://0.0.0.0:8936 + I0405 22:03:17.430371 2655655 rpc.go:254] Received Ping request + ``` + +5. **Check Port Availability**: Confirm that the _AI Subnet_ Orchestrator node is operating on port `8936`. Unblock port `8936` on your machine and set up port forwarding on your router to make the Gateway node accessible from the internet. ### Gateway Setup @@ -213,7 +227,14 @@ Gateway nodes on the _AI Subnet_ can be set up using the [pre-built binaries](ht The flags used here also apply to the [Mainnet transcoding network](https://github.com/livepeer/go-livepeer). To comprehensively understand these flags, consult the [Livepeer documentation](https://docs.livepeer.org/references/go-livepeer/cli-reference). Specifically, the `--orchAddr` and `--httpAddr` flags are crucial for routing the Gateway node to your local Orchestrator (i.e., `0.0.0.0:8936`) and facilitating **off-chain** communication between the Gateway and the Orchestrator. -3. **Verify Setup**: Confirm that the _AI Subnet_ Gateway node is operating on port `8937`. To make the Gateway node accessible from the internet, unblock port `8937` on your machine and set up port forwarding on your router. +3. **Verify Setup**: If your _AI Subnet_ Gateway node is running correctly, you should see the following output: + + ```bash + I0405 22:08:48.667588 2669536 mediaserver.go:226] HTTP Server listening on http://0.0.0.0:8937 + I0405 22:08:48.667607 2669536 lpms.go:92] LPMS Server listening on rtmp://127.0.0.1:1935 + ``` + +4. **Check Port Availability**: Confirm that the _AI Subnet_ Gateway node is operating on port `8937`. Unblock port `8937` on your machine and set up port forwarding on your router to make the Gateway node accessible from the internet. #### Gateway Docker Setup @@ -237,7 +258,14 @@ Gateway nodes on the _AI Subnet_ can be set up using the [pre-built binaries](ht As outlined in the [Gateway Binary Setup](#gateway-binary-setup), the flags are common to the [Mainnet transcoding network](https://github.com/livepeer/go-livepeer) and are documented in the [Livepeer documentation](https://docs.livepeer.org/references/go-livepeer/cli-reference). The `--orchAddr` and `--httpAddr` flags are essential for directing the Gateway node to your local Orchestrator and ensuring **off-chain** communication between the Gateway and the Orchestrator, respectively. -3. **Verify Setup**: Confirm that the _AI Subnet_ Gateway node is operating on port `8937`. To make the Gateway node accessible from the internet, unblock port `8937` on your machine and set up port forwarding on your router. +3. **Verify Setup**: If your _AI Subnet_ Gateway node is running correctly, you should see the following output: + + ```bash + I0405 22:08:48.667588 2669536 mediaserver.go:226] HTTP Server listening on http://0.0.0.0:8937 + I0405 22:08:48.667607 2669536 lpms.go:92] LPMS Server listening on rtmp://127.0.0.1:1935 + ``` + +4. **Check Port Availability**: Confirm that the _AI Subnet_ Gateway node is operating on port `8937`. Unblock port `8937` on your machine and set up port forwarding on your router to make the Gateway node accessible from the internet. #### AI Job Submission @@ -416,11 +444,6 @@ Additionally since the _AI Subnet_ software using [Docker-out-of-Docker](http:// Correctly setting these flags configures your _Mainnet AI Subnet_ Orchestrator to successfully redeem AI tickets **on-chain**. 🎉 -#### Use a ticket Redemption Service - -> [!NOTE] -> Coming soon. - ### On-chain Gateway Setup > [!IMPORTANT] @@ -481,6 +504,23 @@ To help the AI SPE team monitor the _AI Subnet_ performance, you can opt to send -config.file=/etc/promtail/ai_subnet_promtail.yml ``` +6. **Verify Setup**: Confirm that Promtail is running correctly by checking the logs: + + ```bash + docker logs ai_subnet_promtail + ``` + + The output should look like: + + ```bash + level=info ts=2024-04-05T20:19:44.284132548Z caller=promtail.go:133 msg="Reloading configuration file" md5sum=3d2b13da03e519c5fdcbdc424436d38f + level=info ts=2024-04-05T20:19:44.284653164Z caller=server.go:322 http=[::]:80 grpc=[::]:9095 msg="server listening on addresses" + level=info ts=2024-04-05T20:19:44.28473552Z caller=main.go:174 msg="Starting Promtail" version="(version=2.9.4, branch=HEAD, revision=f599ebc535)" + level=warn ts=2024-04-05T20:19:44.284751581Z caller=promtail.go:263 msg="enable watchConfig" + level=info ts=2024-04-05T20:19:49.285276484Z caller=target_group.go:128 msg="added Docker target" containerID=2b8e274905d269f987f1a9ff889c7d82e3bcca3e31cc26084fe5662fb0f2d298 + level=info ts=2024-04-05T20:19:49.285319906Z caller=target_group.go:128 msg="added Docker target" containerID=e611b5dac9ced58bb553c5e42a415f7062c78e202f5a1baf38b223fc033cbb5c + ``` + Thank you for contributing to the AI SPE team's efforts to optimize the _AI Subnet_! 🚀 ### Gateway Metrics From 0ed61b8d2d37e008f8ed8eb46a8999c881039518 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Sat, 13 Apr 2024 10:47:55 +0200 Subject: [PATCH 076/203] ci(ai): add AI issue templates This commit introduces two new AI-specific issue templates, aiming to streamline the routing of AI subnet-related issues and feature requests to the appropriate team. --- .github/ISSUE_TEMPLATE/ai_bug_report.yml | 76 +++++++++++++++++++ .github/ISSUE_TEMPLATE/ai_feature_request.yml | 45 +++++++++++ .github/ISSUE_TEMPLATE/config.yml | 8 ++ 3 files changed, 129 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/ai_bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/ai_feature_request.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/ai_bug_report.yml b/.github/ISSUE_TEMPLATE/ai_bug_report.yml new file mode 100644 index 000000000..b992554a2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/ai_bug_report.yml @@ -0,0 +1,76 @@ +name: AI Bug report +description: Create a report to help us improve. +labels: + - "bug" + - "ai" +body: + - type: markdown + attributes: + value: | + ## Bug report + Please fill out the following information to help us understand your issue. + + > [!IMPORTANT] + > This repository is only related to the core bugs with the AI branch of the go-livepeer software (i.e. `ai-video`). It does not cover bugs related to running AI pipelines and AI models used on the AI subnet. For these issues, please refer to the [AI-worker repository](https://github.com/livepeer/ai-worker/issues/new/choose) + - type: textarea + attributes: + label: Describe the bug + description: A clear and concise description of what the bug is. + validations: + required: true + - type: textarea + attributes: + label: Reproduction steps + description: "How do you trigger this bug? Please walk us through it step by step." + value: | + 1. Go to '...' + 2. Click on '....' + 3. Scroll down to '....' + 4. See error + - type: textarea + attributes: + label: Expected behaviour + description: A clear and concise description of what you expected to happen. + - type: dropdown + id: severity + attributes: + label: Severity + description: "How severe is this bug?" + options: + - Minor + - Major + - Critical + - type: textarea + attributes: + label: Screenshots / Live demo link + description: If applicable, add screenshots to help explain your problem. + placeholder: Paste the image link as markdown image + - type: dropdown + id: os + attributes: + label: OS + description: "What operating system are you using?" + options: + - Windows + - Mac + - Linux + - type: dropdown + id: running_on + attributes: + label: Running on + description: "Where are you running the application?" + options: + - Local + - Docker + - type: input + attributes: + label: AI go-livepeer version + description: "What version of the AI-worker are you using?" + - type: input + attributes: + label: AI go-livepeer commit hash + description: "Could you please provide the commit hash of the `ai-video` branch that you are currently using?" + - type: textarea + attributes: + label: Additional context + description: Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/ai_feature_request.yml b/.github/ISSUE_TEMPLATE/ai_feature_request.yml new file mode 100644 index 000000000..22ddfa71f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/ai_feature_request.yml @@ -0,0 +1,45 @@ +name: AI Feature request +description: Suggest an idea for this project. +labels: + - "enhancement" + - "ai" +body: + - type: markdown + attributes: + value: | + ## Feature Request + Please fill out the following information to help us understand your request. + + > [!IMPORTANT] + > This repository is only related to feature requests related to the the AI branch of the go-livepeer software (i.e. `ai-video`). It does not cover feature requests related to the addition of new AI pipelines and AI models used on the AI subnet. For these issues, please refer to the [AI-worker repository](https://github.com/livepeer/ai-worker/issues/new/choose). + - type: textarea + attributes: + label: Is your feature request related to a problem? Please describe. + description: + A clear and concise description of what the problem is. Ex. I'm always + frustrated when [...] + validations: + required: true + - type: textarea + attributes: + label: Describe the solution you'd like + description: A clear and concise description of what you want to happen. + - type: textarea + attributes: + label: Describe alternatives you've considered + description: + A clear and concise description of any alternative solutions or features + you've considered. + - type: textarea + attributes: + label: Use Case + description: "Please describe why you want this feature to be added. This will help us prioritize your request." + - type: textarea + attributes: + label: Expected Outcome + description: "What do you expect to happen once this feature is implemented?" + - type: textarea + attributes: + label: Additional context + description: + Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..4ee3f97db --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: true +contact_links: + - name: Go-livepeer Question + url: https://github.com/livepeer/go-livepeer/discussions + about: Please ask and answer questions related to the go-livepeer software here. + - name: Livepeer Question + url: https://discord.gg/livepeer + about: "Have a general Livepeer question? Join us in the Livepeer Discord server. We're here to help!" From f76726d856606828169a9d180b5edc19c059eba3 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Sat, 13 Apr 2024 11:13:08 +0200 Subject: [PATCH 077/203] ci(ai): add AI pull request labeler This commit adds a pull request labeler action that automatically attaches the `ai` label when a pull request is created to the `ai-video` branch. --- .github/workflows/issue-labeler.yml | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/.github/workflows/issue-labeler.yml b/.github/workflows/issue-labeler.yml index b647014f3..92a718666 100644 --- a/.github/workflows/issue-labeler.yml +++ b/.github/workflows/issue-labeler.yml @@ -1,11 +1,13 @@ -name: Label issues +name: Label issues and pull requests on: issues: - types: - - reopened - - opened + types: [opened, reopened] + pull_request: + types: [opened, reopened] + jobs: label_issues: + if: ${{ github.event_name == 'issues' }} runs-on: ubuntu-latest permissions: issues: write @@ -16,3 +18,12 @@ jobs: add-labels: "status: triage" repo-token: ${{ secrets.GITHUB_TOKEN }} ignore-if-assigned: false + + label_pull_requests: + if: ${{ github.event_name == 'pull_request' }} + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - uses: actions/labeler@v5 From 065edbc4bb9c1a8187f7d7e57309f1286b5d0646 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Mon, 15 Apr 2024 14:17:34 +0200 Subject: [PATCH 078/203] ci: change issue template order This commit ensures that the main branch issue templates are put above the AI related issue templates. --- .github/ISSUE_TEMPLATE/{bug_report.md => 01-bug_report.md} | 0 .../ISSUE_TEMPLATE/{feature_request.md => 02-feature_request.md} | 0 .github/ISSUE_TEMPLATE/{spec.md => 03-spec.md} | 0 .../ISSUE_TEMPLATE/{ai_bug_report.yml => bug_report_ai_video.yml} | 0 .../{ai_feature_request.yml => feature_request_ai.yml} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename .github/ISSUE_TEMPLATE/{bug_report.md => 01-bug_report.md} (100%) rename .github/ISSUE_TEMPLATE/{feature_request.md => 02-feature_request.md} (100%) rename .github/ISSUE_TEMPLATE/{spec.md => 03-spec.md} (100%) rename .github/ISSUE_TEMPLATE/{ai_bug_report.yml => bug_report_ai_video.yml} (100%) rename .github/ISSUE_TEMPLATE/{ai_feature_request.yml => feature_request_ai.yml} (100%) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/01-bug_report.md similarity index 100% rename from .github/ISSUE_TEMPLATE/bug_report.md rename to .github/ISSUE_TEMPLATE/01-bug_report.md diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/02-feature_request.md similarity index 100% rename from .github/ISSUE_TEMPLATE/feature_request.md rename to .github/ISSUE_TEMPLATE/02-feature_request.md diff --git a/.github/ISSUE_TEMPLATE/spec.md b/.github/ISSUE_TEMPLATE/03-spec.md similarity index 100% rename from .github/ISSUE_TEMPLATE/spec.md rename to .github/ISSUE_TEMPLATE/03-spec.md diff --git a/.github/ISSUE_TEMPLATE/ai_bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report_ai_video.yml similarity index 100% rename from .github/ISSUE_TEMPLATE/ai_bug_report.yml rename to .github/ISSUE_TEMPLATE/bug_report_ai_video.yml diff --git a/.github/ISSUE_TEMPLATE/ai_feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request_ai.yml similarity index 100% rename from .github/ISSUE_TEMPLATE/ai_feature_request.yml rename to .github/ISSUE_TEMPLATE/feature_request_ai.yml From 1019d429eca743218fe133602b5f4344e4766308 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Mon, 15 Apr 2024 14:27:11 +0200 Subject: [PATCH 079/203] ci(ai): add PR labeler config file This commmit adds a https://github.com/actions/labeler configuration file so that all PRs on the `ai-video` branch will be correctly labeled with the `ai` label. --- .github/labeler.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .github/labeler.yml diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 000000000..4e219d6f4 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,2 @@ +ai: + - base-branch: "ai-video" From af6186fd1fc9928b996903f61140b13e70564cf8 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Mon, 15 Apr 2024 16:28:42 +0200 Subject: [PATCH 080/203] ci(ai): fix incorrect labels This commit fixed the labels that were specified in the Issue Templates to the one found in the repository. --- .github/ISSUE_TEMPLATE/bug_report_ai_video.yml | 4 ++-- .github/ISSUE_TEMPLATE/feature_request_ai.yml | 4 ++-- .github/labeler.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report_ai_video.yml b/.github/ISSUE_TEMPLATE/bug_report_ai_video.yml index b992554a2..b8a7c25dd 100644 --- a/.github/ISSUE_TEMPLATE/bug_report_ai_video.yml +++ b/.github/ISSUE_TEMPLATE/bug_report_ai_video.yml @@ -1,8 +1,8 @@ name: AI Bug report description: Create a report to help us improve. labels: - - "bug" - - "ai" + - "type: bug" + - "AI" body: - type: markdown attributes: diff --git a/.github/ISSUE_TEMPLATE/feature_request_ai.yml b/.github/ISSUE_TEMPLATE/feature_request_ai.yml index 22ddfa71f..0c708f110 100644 --- a/.github/ISSUE_TEMPLATE/feature_request_ai.yml +++ b/.github/ISSUE_TEMPLATE/feature_request_ai.yml @@ -1,8 +1,8 @@ name: AI Feature request description: Suggest an idea for this project. labels: - - "enhancement" - - "ai" + - "type: feature" + - "AI" body: - type: markdown attributes: diff --git a/.github/labeler.yml b/.github/labeler.yml index 4e219d6f4..2e2c7b286 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,2 +1,2 @@ -ai: +AI: - base-branch: "ai-video" From 51406be0935b156182dfe4bb399c4b4b72610b3c Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Tue, 16 Apr 2024 07:56:47 +0200 Subject: [PATCH 081/203] ci: rename labeler and remove trailing whitespace --- .github/workflows/{issue-labeler.yml => labeler.yml} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename .github/workflows/{issue-labeler.yml => labeler.yml} (95%) diff --git a/.github/workflows/issue-labeler.yml b/.github/workflows/labeler.yml similarity index 95% rename from .github/workflows/issue-labeler.yml rename to .github/workflows/labeler.yml index 92a718666..0c45d7086 100644 --- a/.github/workflows/issue-labeler.yml +++ b/.github/workflows/labeler.yml @@ -18,7 +18,7 @@ jobs: add-labels: "status: triage" repo-token: ${{ secrets.GITHUB_TOKEN }} ignore-if-assigned: false - + label_pull_requests: if: ${{ github.event_name == 'pull_request' }} runs-on: ubuntu-latest @@ -26,4 +26,4 @@ jobs: contents: read pull-requests: write steps: - - uses: actions/labeler@v5 + - uses: actions/labeler@v5 From 9502ea0a5c90390d0f34c844e104150eb5eaaa31 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Tue, 16 Apr 2024 16:19:15 +0200 Subject: [PATCH 082/203] feat(ai): add pipelines optimization flags (#3013) * feat(ai): add pipelines optimization flags This commit adds a new `OptimizationFlags` field to the `aiModels` config so that users can forward optimization environment variables to the [ai-worker](git@github.com:livepeer/ai-worker.git) for more information see https://github.com/livepeer/ai-worker/pull/61. * chore: update ai-worker to latest commit This commit ensures that the https://github.com/livepeer/ai-worker dependency is on the latest commit that includes the new optimization flags feature (see https://github.com/livepeer/ai-worker/pull/61). * refactor: improve OptFlags logging This commit ensures that the `optimzation flag not supported` warning is shown for each model that is not loaded warm. --- cmd/livepeer/starter/starter.go | 7 ++++++- core/ai.go | 17 +++++++++-------- go.mod | 4 ++-- go.sum | 10 ++-------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index 79367475b..590edaebd 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -545,12 +545,17 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { // the endpoint for an external container if config.Warm || config.URL != "" { endpoint := worker.RunnerEndpoint{URL: config.URL, Token: config.Token} - if err := n.AIWorker.Warm(ctx, config.Pipeline, config.ModelID, endpoint); err != nil { + if err := n.AIWorker.Warm(ctx, config.Pipeline, config.ModelID, endpoint, config.OptimizationFlags); err != nil { glog.Errorf("Error AI worker warming %v container: %v", config.Pipeline, err) return } } + // Show warning if people set OptimizationFlags but not Warm. + if len(config.OptimizationFlags) > 0 && !config.Warm { + glog.Warningf("Model %v has 'optimization_flags' set without 'warm'. Optimization flags are currently only used for warm containers.", config.ModelID) + } + switch config.Pipeline { case "text-to-image": _, ok := constraints[core.Capability_TextToImage] diff --git a/core/ai.go b/core/ai.go index 384762c3c..4a7a5ecd0 100644 --- a/core/ai.go +++ b/core/ai.go @@ -15,18 +15,19 @@ type AI interface { TextToImage(context.Context, worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) ImageToImage(context.Context, worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) ImageToVideo(context.Context, worker.ImageToVideoMultipartRequestBody) (*worker.VideoResponse, error) - Warm(context.Context, string, string, worker.RunnerEndpoint) error + Warm(context.Context, string, string, worker.RunnerEndpoint, worker.OptimizationFlags) error Stop(context.Context) error } type AIModelConfig struct { - Pipeline string `json:"pipeline"` - ModelID string `json:"model_id"` - URL string `json:"url,omitempty"` - Token string `json:"token,omitempty"` - Warm bool `json:"warm,omitempty"` - PricePerUnit int64 `json:"price_per_unit,omitempty"` - PixelsPerUnit int64 `json:"pixels_per_unit,omitempty"` + Pipeline string `json:"pipeline"` + ModelID string `json:"model_id"` + URL string `json:"url,omitempty"` + Token string `json:"token,omitempty"` + Warm bool `json:"warm,omitempty"` + PricePerUnit int64 `json:"price_per_unit,omitempty"` + PixelsPerUnit int64 `json:"pixels_per_unit,omitempty"` + OptimizationFlags worker.OptimizationFlags `json:"optimization_flags,omitempty"` } func (config *AIModelConfig) UnmarshalJSON(data []byte) error { diff --git a/go.mod b/go.mod index 3ef60906c..339f8805a 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.0-20240220213200-59b5b237cd8b + github.com/livepeer/ai-worker v0.0.0-20240416102817-db751cd47fdc github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 @@ -32,6 +32,7 @@ require ( go.uber.org/goleak v1.3.0 golang.org/x/net v0.19.0 google.golang.org/grpc v1.57.1 + google.golang.org/protobuf v1.31.0 pgregory.net/rapid v1.1.0 ) @@ -197,7 +198,6 @@ require ( google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect - google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index d4b9a93d3..a19f36cfb 100644 --- a/go.sum +++ b/go.sum @@ -532,14 +532,8 @@ github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4n github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/livepeer/ai-worker v0.0.0-20240213170524-1c860ab6e412 h1:zc6XbU0KvJ8C2jDdsZP8IkWeKN0TCQMIUyl3fOEydoU= -github.com/livepeer/ai-worker v0.0.0-20240213170524-1c860ab6e412/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= -github.com/livepeer/ai-worker v0.0.0-20240214164547-fbdca26a9b2d h1:YpBW6wqwpQ9ffgXPnCEoMbsQEbCT5G2AbTb91jrD6BU= -github.com/livepeer/ai-worker v0.0.0-20240214164547-fbdca26a9b2d/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= -github.com/livepeer/ai-worker v0.0.0-20240214223314-3d355743417a h1:LZwlUatQLU7rkICGMmca71DE67iGhln/QttAehEs8LA= -github.com/livepeer/ai-worker v0.0.0-20240214223314-3d355743417a/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= -github.com/livepeer/ai-worker v0.0.0-20240220213200-59b5b237cd8b h1:zCQv/A3Pafdr/NutU7zLChkF+XcAdzY0ObjN1E4nr44= -github.com/livepeer/ai-worker v0.0.0-20240220213200-59b5b237cd8b/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= +github.com/livepeer/ai-worker v0.0.0-20240416102817-db751cd47fdc h1:wmXT+pkcAshQ5yYiDLpoeIY0+P3Ob4RxHOljbS13FfA= +github.com/livepeer/ai-worker v0.0.0-20240416102817-db751cd47fdc/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= From cea4e94227a093ea94febeb1b3b16b4f274b5eeb Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Tue, 16 Apr 2024 16:36:00 +0200 Subject: [PATCH 083/203] docs(ai): add optimization flags to docs (#3014) This commit adds a new section explaining the new `optimization_flags` that were enabled https://github.com/livepeer/go-livepeer/pull/3013. --- doc/ai-subnet.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/doc/ai-subnet.md b/doc/ai-subnet.md index 267e2efc5..611ae2b7e 100644 --- a/doc/ai-subnet.md +++ b/doc/ai-subnet.md @@ -64,6 +64,8 @@ For testing and development purposes, it's a good practice first to run the Orch #### AI Models Configuration +##### How to Configure + Orchestrators on the _AI Subnet_ can select the [supported models](#supported-ai-models) they wish to advertise and process. To do this: 1. **AI Model Configuration**: Create an `aiModels.json` file in the `~/.lpData` directory to specify the AI models to support in the _AI Subnet_. Refer to the provided example below for proper formatting: @@ -84,7 +86,11 @@ Orchestrators on the _AI Subnet_ can select the [supported models](#supported-ai { "pipeline": "image-to-video", "model_id": "stabilityai/stable-video-diffusion-img2vid-xt-1-1", - "price_per_unit": 3390842 + "price_per_unit": 3390842, + "warm": true, + "optimization_flags": { + "SFAST": true + } } ] ``` @@ -96,6 +102,7 @@ Orchestrators on the _AI Subnet_ can select the [supported models](#supported-ai - `price_per_unit`: This mandatory field is the price in [Wei](https://ethdocs.org/en/latest/ether.html) per unit of work. - `warm`: By default, the Livepeer software dynamically loads the model onto the GPU as needed. However, if you set this flag to `true`, the model will be preloaded onto the GPU when the Orchestrator starts and will remain there, a state referred to as 'warm'. This approach reduces the model's runtime as it's already loaded onto the GPU when requests com in. Please note that in our current **alpha** phase, we only support one model per GPU. Consequently, if you have one GPU and one 'warm' model, you won't be able to serve any other models. - `warm`: By default, the Livepeer software loads the AI model onto the GPU on-demand. If you set this flag to `true`, the model is preloaded onto the GPU when the Orchestrator starts, keeping it 'warm'. This means it stays loaded on the GPU, reducing the model's execution time as it's immediately available when requests come in. However, in our current **alpha** phase, we support only one model per GPU. So, if you have a single GPU and a 'warm' model, you won't be able to serve additional models. + - `optimization_flags`: This optional field allows you to enable specific optimizations for the pipeline (see [Optimization Flags](#optimization-flags)). 2. **Install Hugging Face CLI**: Install the Hugging Face CLI by running the following command: @@ -117,6 +124,17 @@ Orchestrators on the _AI Subnet_ can select the [supported models](#supported-ai > [!NOTE] > The `--alpha` flag is used to download only the models currently supported by the Livepeer.inc Gateway node on the _AI Subnet_. You can remove this flag if you want to download all models and advertise them for other Gateway nodes. +##### Optimization Flags + +> [!WARNING] +> The flags described below are experimental and may not function as anticipated. If you encounter any issues, please report them to the [go-livepeer](https://github.com/livepeer/go-livepeer/issues/new/choose) repository. + +The `optimization_flags` attribute in the `aiModels.json` file provides a way to activate specific performance enhancements for the pipeline. Currently, the following flags are available: + +- `SFAST`: Enables the [stable-fast](https://github.com/chengzeyi/stable-fast) optimization, enhancing inference performance. + - **Usage**: Add `"SFAST": true` to the `optimization_flags` section in the `aiModels.json` file. + - **Limitations**: Currently, the `SFAST` flag is only effective for `warm` models in the `image-to-video` pipeline. While it accelerates inference times by approximately 25%, it also extends the Node startup time. + #### Orchestrator Binary Setup To run the _AI Subnet_ Orchestrator **off-chain** using the [pre-build binaries](https://discord.com/channels/423160867534929930/577736983036559360), follow these steps: From bc629b775cdb566e0b89706b4fb805f5093c3b6d Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Tue, 16 Apr 2024 16:43:41 +0200 Subject: [PATCH 084/203] ci(ai): temporary change build action branch to ai-video This commit temporary changes the push branch of the `build.yml` to the `ai-video` branch since the `ai-video` branch has conflicts with the `master` branch preventing the containers to be build. --- .github/workflows/build.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 10daec16d..c38626e83 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -4,7 +4,8 @@ on: pull_request: push: branches: - - master + # - master + - ai-video tags: - "v*" From 6aa0b004db8eeaf1043316ddf410317d50211c59 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Tue, 16 Apr 2024 16:48:04 +0200 Subject: [PATCH 085/203] ci(ai): temporary change docker action branch to ai-video This commit temporary changes the push branch of the `docker.yml` to the `ai-video` branch since the `ai-video` branch has conflicts with the `master` branch preventing the containers to be build. --- .github/workflows/docker.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 8d75b7ab5..a1ad605d7 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -4,7 +4,8 @@ on: pull_request: push: branches: - - master + # - master + - ai-video tags: - "v*" From cecd3a5bbf1e5e7e30b8b90ed68c2595054a4424 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 18 Apr 2024 15:29:18 +0200 Subject: [PATCH 086/203] ci(ai): fix pull request config warning (#3018) This commit gets rid of the Pull request labeler configuration file warning. --- .github/workflows/labeler.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 0c45d7086..794fb2078 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -26,4 +26,5 @@ jobs: contents: read pull-requests: write steps: + - uses: actions/checkout@v4 - uses: actions/labeler@v5 From 2a782ed49e3f9fa19de560692868102f5e24a130 Mon Sep 17 00:00:00 2001 From: Marco van Dijk Date: Thu, 18 Apr 2024 17:09:52 +0200 Subject: [PATCH 087/203] fix: flush writer when encoding AI results (fix invalid PNG) (#3020) This commit flushes the data in the image writer to ensure that all data gets written to the PNG. --- server/ai_process.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/server/ai_process.go b/server/ai_process.go index 14cb190e2..54092c737 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -49,9 +49,11 @@ func processTextToImage(ctx context.Context, params aiRequestParams, req worker. newMedia := make([]worker.Media, len(resp.Images)) for i, media := range resp.Images { var data bytes.Buffer - if err := worker.ReadImageB64DataUrl(media.Url, bufio.NewWriter(&data)); err != nil { + writer := bufio.NewWriter(&data) + if err := worker.ReadImageB64DataUrl(media.Url, writer); err != nil { return nil, err } + writer.Flush() name := string(core.RandomManifestID()) + ".png" newUrl, err := params.os.SaveData(ctx, name, bytes.NewReader(data.Bytes()), nil, 0) @@ -116,9 +118,11 @@ func processImageToImage(ctx context.Context, params aiRequestParams, req worker newMedia := make([]worker.Media, len(resp.Images)) for i, media := range resp.Images { var data bytes.Buffer - if err := worker.ReadImageB64DataUrl(media.Url, bufio.NewWriter(&data)); err != nil { + writer := bufio.NewWriter(&data) + if err := worker.ReadImageB64DataUrl(media.Url, writer); err != nil { return nil, err } + writer.Flush() name := string(core.RandomManifestID()) + ".png" newUrl, err := params.os.SaveData(ctx, name, bytes.NewReader(data.Bytes()), nil, 0) From e1db2396fbe16507e98911b07f1c8af8e67d349a Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 18 Apr 2024 18:32:18 +0200 Subject: [PATCH 088/203] ci(ai): add myself as branch CODE OWNER --- .github/CODEOWNERS | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..3ba282ed1 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,3 @@ +# Default reviewers for the `ai-video` branch. +# TODO: Change if merged into `master` branch. +* @rickstaa From 1643a1ebf72af50f227d26776ff595f83d0d4747 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 18 Apr 2024 18:43:27 +0200 Subject: [PATCH 089/203] ci(ai): run labeler also on 'pull_request_target' This commit ensures that the labeler action also runs on a 'pull_request_target' to ensure pull requests from forks are correctly labeled. --- .github/workflows/labeler.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 794fb2078..a34778a17 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -4,6 +4,8 @@ on: types: [opened, reopened] pull_request: types: [opened, reopened] + pull_request_target: + types: [opened, reopened] jobs: label_issues: From 5bb92faf17a99bcecee2b3a10526735a54732ad4 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 18 Apr 2024 18:51:12 +0200 Subject: [PATCH 090/203] ci(ai): cleanup labeler actions --- .../workflows/{labeler.yml => issue-labeler.yml} | 16 +--------------- .github/workflows/pr-labeler.yml | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 15 deletions(-) rename .github/workflows/{labeler.yml => issue-labeler.yml} (50%) create mode 100644 .github/workflows/pr-labeler.yml diff --git a/.github/workflows/labeler.yml b/.github/workflows/issue-labeler.yml similarity index 50% rename from .github/workflows/labeler.yml rename to .github/workflows/issue-labeler.yml index a34778a17..390535f95 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/issue-labeler.yml @@ -1,11 +1,7 @@ -name: Label issues and pull requests +name: Label issues on: issues: types: [opened, reopened] - pull_request: - types: [opened, reopened] - pull_request_target: - types: [opened, reopened] jobs: label_issues: @@ -20,13 +16,3 @@ jobs: add-labels: "status: triage" repo-token: ${{ secrets.GITHUB_TOKEN }} ignore-if-assigned: false - - label_pull_requests: - if: ${{ github.event_name == 'pull_request' }} - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: write - steps: - - uses: actions/checkout@v4 - - uses: actions/labeler@v5 diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml new file mode 100644 index 000000000..6bb3aa685 --- /dev/null +++ b/.github/workflows/pr-labeler.yml @@ -0,0 +1,14 @@ +name: Label issues +on: + pull_request_target: + types: [opened, reopened] + +jobs: + label_pull_requests: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - uses: actions/checkout@v4 + - uses: actions/labeler@v5 From 23fdfcb151cb72709c9e485e95a0e9f01d9c3aea Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 18 Apr 2024 19:01:21 +0200 Subject: [PATCH 091/203] ci(ai): auto assign AI issues and feature requests This commit ensures that all AI related issues and feature requests are assigned to the AI team. --- .github/ISSUE_TEMPLATE/bug_report_ai_video.yml | 2 ++ .github/ISSUE_TEMPLATE/feature_request_ai.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report_ai_video.yml b/.github/ISSUE_TEMPLATE/bug_report_ai_video.yml index b8a7c25dd..bd8565982 100644 --- a/.github/ISSUE_TEMPLATE/bug_report_ai_video.yml +++ b/.github/ISSUE_TEMPLATE/bug_report_ai_video.yml @@ -3,6 +3,8 @@ description: Create a report to help us improve. labels: - "type: bug" - "AI" +assignees: + - rickstaa body: - type: markdown attributes: diff --git a/.github/ISSUE_TEMPLATE/feature_request_ai.yml b/.github/ISSUE_TEMPLATE/feature_request_ai.yml index 0c708f110..648117fa7 100644 --- a/.github/ISSUE_TEMPLATE/feature_request_ai.yml +++ b/.github/ISSUE_TEMPLATE/feature_request_ai.yml @@ -3,6 +3,8 @@ description: Suggest an idea for this project. labels: - "type: feature" - "AI" +assignees: + - rickstaa body: - type: markdown attributes: From e9258f301ca923d2acca9bdc8cd6d3263f4a2dbc Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 18 Apr 2024 20:23:35 +0200 Subject: [PATCH 092/203] feat(ai): enable AI orchestrator discovery (#3004) * feat(ai): enable AI orchestrator discovery This commit incorporates the AIServiceRegistry contract address, superseding the conventional ServiceRegistry contract address. This strategic alteration streamlines the discovery process of AI Orchestrators within the AI Subnet, thereby bolstering network accessibility and interaction. While this approach serves as a swift workaround to enable the feature without extensive code modification, it's important to note that it may disrupt the existing transcoding discovery mechanism. We have to fix this if we want to merge the two networks in the future. * docs(ai): improve discovery documentation This commit ensures that people are aware that they have to interact with the `AIServiceRegistry` using their main Orch wallet. * fix: fix 'AIServiceRegistry' devnet and testnet issue This commit ensure that the hardcoded `AIServiceRegistry` contract doesn't break the go-livepeer binary on local devnets or testnets. --- doc/ai-subnet.md | 42 ++++++++++++++++++++++++++++++++++++++++++ eth/client.go | 17 +++++++++++++++-- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/doc/ai-subnet.md b/doc/ai-subnet.md index 611ae2b7e..da3d64713 100644 --- a/doc/ai-subnet.md +++ b/doc/ai-subnet.md @@ -387,6 +387,48 @@ The first and **recommended method** is to use the `ethOrchAddr` flag to set the After you have completed these steps, you can start your _Mainnet AI Subnet_ Orchestrator using the binary or Docker image. +#### Allow your AI Subnet Orchestrator to be Discovered + +To ensure that _AI Gateway_ nodes can discover your _AI Subnet_ Orchestrator, you need to write your service URI to the blockchain. This URI should be in the format `https://:`. To write your service URI to the blockchain, go to the machine that contains your _Mainnet AI Subnet_ Orchestrator wallet and follow these steps: + +1. Install the [Foundry](https://book.getfoundry.sh/getting-started/installation) smart contract development toolchain: + + ```bash + curl -L https://foundry.paradigm.xyz | bash + source /home//.bashrc + foundryup + ``` + +2. Invoke the `SetServiceURI` function on the [AIServiceRegistry](https://arbiscan.io/address/0x04C0b249740175999E5BF5c9ac1dA92431EF34C5) contract. Use the KeyStore file and password from your _Mainnet Transcoding Network_ Orchestrator wallet to execute this command: + + ```bash + cast send --keystore '' --password '' --rpc-url 0x04C0b249740175999E5BF5c9ac1dA92431EF34C5 "setServiceURI(string)" https://: + ``` + + If everything was successful, you should see the following output: + + ```bash + blockHash 0x214a65d2dffd1732e971bd3662dcb681663c2eb0c95a33c8918bab0a44e2d3ed + blockNumber 200370198 + contractAddress + cumulativeGasUsed 555214 + effectiveGasPrice 11048000 + ``` + +3. To check if your service URI was successfully written to the blockchain, call the `getServiceURI` function on the [AIServiceRegistry](https://arbiscan.io/address/0x5d31637eb0f442376053d5dea2347f663c4019dc) contract using the following command: + + ```bash + cast call --rpc-url 0x04C0b249740175999E5BF5c9ac1dA92431EF34C5 "getServiceURI(address)" | xxd -r -p + ``` + + If successful, you should see the following output: + + ```bash + https://: + ``` + +Congratulations! Your _Mainnet AI Subnet_ Orchestrator is now set up to be discovered by _AI Gateway_ nodes on the _AI Subnet_. 🚀 + #### Binary Startup Command To start your _Mainnet AI Subnet_ Orchestrator using the [pre-built binaries](https://discord.com/channels/423160867534929930/577736983036559360), use the following command: diff --git a/eth/client.go b/eth/client.go index e57ab112b..445206289 100644 --- a/eth/client.go +++ b/eth/client.go @@ -211,12 +211,25 @@ func (c *client) setContracts(opts *bind.TransactOpts) error { glog.V(common.SHORT).Infof("LivepeerToken: %v", c.tokenAddr.Hex()) - serviceRegistryAddr, err := c.GetContract(crypto.Keccak256Hash([]byte("ServiceRegistry"))) + chainID, err := c.backend.ChainID(context.Background()) if err != nil { - glog.Errorf("Error getting ServiceRegistry address: %v", err) + glog.Errorf("Failed to get chain ID from remote ethereum node: %v", err) return err } + // TODO: This is a temporary setup for a separate AIServiceRegistry. Revise this when AI subnet merges with the mainnet. + var serviceRegistryAddr ethcommon.Address + arbitrumOneChainID := big.NewInt(42161) + if chainID.Cmp(arbitrumOneChainID) == 0 { + serviceRegistryAddr = ethcommon.HexToAddress("0x04C0b249740175999E5BF5c9ac1dA92431EF34C5") + } else { + serviceRegistryAddr, err = c.GetContract(crypto.Keccak256Hash([]byte("ServiceRegistry"))) + if err != nil { + glog.Errorf("Error getting ServiceRegistry address: %v", err) + return err + } + } + c.serviceRegistryAddr = serviceRegistryAddr serviceRegistry, err := contracts.NewServiceRegistry(serviceRegistryAddr, c.backend) From bfccbc4f3dececba0e4da7f640e70db80f172061 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Sat, 20 Apr 2024 15:07:11 +0200 Subject: [PATCH 093/203] refactor(ai): add extra devtool input arguments (#3026) This commit adds extra devtool input arguments allowing developers to spin up multiple Os on the ETH devnet. --- cmd/devtool/README.md | 17 ++++++------- cmd/devtool/devtool.go | 38 ++++++++++++++++++++++++++-- cmd/devtool/devtool/devtool_utils.go | 14 +++++----- 3 files changed, 52 insertions(+), 17 deletions(-) diff --git a/cmd/devtool/README.md b/cmd/devtool/README.md index 71e95928e..ead5a654e 100644 --- a/cmd/devtool/README.md +++ b/cmd/devtool/README.md @@ -2,31 +2,30 @@ An on-chain workflow testing tool that supports the following: -- Automatically submitting the necessary setup transactions for each node type -- Generating a Bash script with default CLI flags to start each node type +- Automatically submitting the necessary setup transactions for each node type +- Generating a Bash script with default CLI flags to start each node type ## Prerequisites -## Step 1: Set up a private ETH network with Livepeer protocol deployed +### Step 1: Set up a private ETH network with Livepeer protocol deployed -``` +```bash docker pull livepeer/geth-with-livepeer-protocol:confluence docker run -p 8545:8545 -p 8546:8546 --name geth-with-livepeer-protocol livepeer/geth-with-livepeer-protocol:confluence ``` - -## Step 2: Set up a broadcaster +### Step 2: Set up a broadcaster `go run cmd/devtool/devtool.go setup broadcaster` This command will submit the setup transactions for a broadcaster and generate the Bash script `run_broadcaster_.sh` which can be used to start a broadcaster node. -## Step 3: Set up a orchestrator/transcoder +### Step 3: Set up a orchestrator/transcoder `go run cmd/devtool/devtool.go setup transcoder` This command will submit the setup transactions for an orchestrator/transcoder and generate the Bash scripts: -* `run_orchestrator_with_transcoder_.sh` which can be used to start an orchestrator node that contains a transcoder (combined OT) -* `run_orchestrator_standalone_.sh` and `run_transcoder_.sh` which can be used to start separate orchestrator and transcoder nodes (split O/T) +- `run_orchestrator_with_transcoder_.sh` which can be used to start an orchestrator node that contains a transcoder (combined OT) +- `run_orchestrator_standalone_.sh` and `run_transcoder_.sh` which can be used to start separate orchestrator and transcoder nodes (split O/T) diff --git a/cmd/devtool/devtool.go b/cmd/devtool/devtool.go index e106e3044..038313350 100644 --- a/cmd/devtool/devtool.go +++ b/cmd/devtool/devtool.go @@ -3,14 +3,16 @@ package main import ( "flag" "fmt" - "github.com/golang/glog" - "github.com/livepeer/go-livepeer/cmd/devtool/devtool" "io" "io/ioutil" + "math/big" "os" "path/filepath" "strconv" "strings" + + "github.com/golang/glog" + "github.com/livepeer/go-livepeer/cmd/devtool/devtool" ) var ( @@ -27,6 +29,10 @@ func main() { miningAccountFlag := flag.String("miningaccount", "", "Override geth mining account (usually not needed)") ethControllerFlag := flag.String("controller", "", "Override controller address (usually not needed)") svcHost := flag.String("svchost", "127.0.0.1", "default service host") + cliPortStr := flag.String("cliport", "", "CLI port") + mediaPortStr := flag.String("mediaport", "", "Media port") + rtmpPortStr := flag.String("rtmpport", "", "RTMP port") + bondAmount := flag.String("bond", "500", "Orchestrator bonded amount in LPT") flag.Parse() @@ -46,6 +52,34 @@ func main() { serviceHost = *svcHost cfg.ServiceURI = fmt.Sprintf("https://%s:", serviceHost) } + if *cliPortStr != "" { + cliPortTmp, err := strconv.Atoi(*cliPortStr) + if err != nil { + glog.Errorf("Invalid cli port %v", *cliPortStr) + } + cliPort = cliPortTmp + } + if *mediaPortStr != "" { + mediaPortTmp, err := strconv.Atoi(*mediaPortStr) + if err != nil { + glog.Errorf("Invalid media port %v", *mediaPortStr) + } + mediaPort = mediaPortTmp + } + if *rtmpPortStr != "" { + rtmpPortTmp, err := strconv.Atoi(*rtmpPortStr) + if err != nil { + glog.Errorf("Invalid rtmp port %v", *rtmpPortStr) + } + rtmpPort = rtmpPortTmp + } + if *bondAmount != "" { + cfg.BondAmount = new(big.Int) + _, ok := cfg.BondAmount.SetString(*bondAmount, 10) + if !ok { + glog.Exitf("Invalid bond amount %v", *bondAmount) + } + } args := flag.Args() goodToGo := false isBroadcaster := true diff --git a/cmd/devtool/devtool/devtool_utils.go b/cmd/devtool/devtool/devtool_utils.go index b58b2657d..b8d822398 100644 --- a/cmd/devtool/devtool/devtool_utils.go +++ b/cmd/devtool/devtool/devtool_utils.go @@ -5,6 +5,12 @@ import ( "context" "errors" "fmt" + "io/ioutil" + "math/big" + "os" + "strings" + "time" + "github.com/ethereum/go-ethereum/accounts/keystore" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/console" @@ -13,11 +19,6 @@ import ( "github.com/ethereum/go-ethereum/rpc" "github.com/golang/glog" "github.com/livepeer/go-livepeer/eth" - "io/ioutil" - "math/big" - "os" - "strings" - "time" ) const ( @@ -35,6 +36,7 @@ type DevtoolConfig struct { Account string KeystoreDir string IsBroadcaster bool + BondAmount *big.Int } func NewDevtoolConfig() DevtoolConfig { @@ -300,7 +302,7 @@ func (d *Devtool) RegisterOrchestrator(cfg DevtoolConfig) error { // curl -d "blockRewardCut=10&feeShare=5&amount=500" --data-urlencode "serviceURI=https://$transcoderServiceAddr" \ // -H "Content-Type: application/x-www-form-urlencoded" \ // -X "POST" http://localhost:$transcoderCliPort/activateTranscoder\ - var amount *big.Int = big.NewInt(int64(500)) + var amount = cfg.BondAmount glog.Infof("Bonding %v to %s", amount, cfg.Account) tx, err := d.Client.Bond(amount, ethcommon.HexToAddress(cfg.Account)) From 865314d24f1cb3ceded03b3181525a201cb01af5 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Sat, 20 Apr 2024 16:19:18 +0200 Subject: [PATCH 094/203] chore: improve devtool documentation and add scripts This commit improves the devtool documentation and adds a helpful script if developers want to create multiple Os at the same time. --- cmd/devtool/README.md | 12 +++++++++ .../scripts/create_multiple_transcoders.bash | 26 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 cmd/devtool/scripts/create_multiple_transcoders.bash diff --git a/cmd/devtool/README.md b/cmd/devtool/README.md index ead5a654e..7710a0a07 100644 --- a/cmd/devtool/README.md +++ b/cmd/devtool/README.md @@ -7,6 +7,12 @@ An on-chain workflow testing tool that supports the following: ## Prerequisites +- [Docker](https://docs.docker.com/get-docker/) +- [Go](https://golang.org/doc/install) +- [Go-livepeer](https://github.com/livepeer/go-livepeer) build from source (see [the docs](https://docs.livepeer.org/orchestrators/guides/install-go-livepeer#build-from-source)). + +## Setting Up an On-Chain Development Environment + ### Step 1: Set up a private ETH network with Livepeer protocol deployed ```bash @@ -29,3 +35,9 @@ This command will submit the setup transactions for an orchestrator/transcoder a - `run_orchestrator_with_transcoder_.sh` which can be used to start an orchestrator node that contains a transcoder (combined OT) - `run_orchestrator_standalone_.sh` and `run_transcoder_.sh` which can be used to start separate orchestrator and transcoder nodes (split O/T) + +## Extra Resources + +### Scripts + +Some helpful scripts are provided in the `scripts` directory. diff --git a/cmd/devtool/scripts/create_multiple_transcoders.bash b/cmd/devtool/scripts/create_multiple_transcoders.bash new file mode 100644 index 000000000..633024158 --- /dev/null +++ b/cmd/devtool/scripts/create_multiple_transcoders.bash @@ -0,0 +1,26 @@ +# Script to create multiple transcoders on the ETH devnet. + +CLI_PORT=7935 +MEDIA_PORT=8935 +RTMP_PORT=1935 +BOND=50 + +if [ -z "$1" ]; then + echo "Usage: create_multiple_transcoders.bash " + exit 1 +fi + +num_transcoders=$1 + +# Run the go devtool transcoder script multiple times with different ports +echo "Creating $num_transcoders transcoders..." +for i in $(seq 1 $num_transcoders); do + echo "Creating transcoder $i..." + go run cmd/devtool/devtool.go --cliport $CLI_PORT --mediaport $MEDIA_PORT --rtmpport $RTMP_PORT -bond $BOND setup transcoder + CLI_PORT=$((CLI_PORT+10)) + MEDIA_PORT=$((MEDIA_PORT+10)) + RTMP_PORT=$((RTMP_PORT+10)) + BOND=$((BOND**10)) +done + +echo "Done creating $num_transcoders transcoders" From ea82cde6ce9d317c594518703d00abb74cff82e6 Mon Sep 17 00:00:00 2001 From: Elite Encoder Date: Wed, 24 Apr 2024 06:27:45 -0400 Subject: [PATCH 095/203] refactor: log advertised capabilities and price on startup (#3031) This commit logs the advertised capabilities and price on startup if users have their logging verbosity level set to 6 or higher. --- cmd/livepeer/starter/starter.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index 590edaebd..a149ce62e 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -594,6 +594,12 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { n.SetBasePriceForCap("default", core.Capability_ImageToVideo, config.ModelID, big.NewRat(config.PricePerUnit, config.PixelsPerUnit)) } + + if len(aiCaps) > 0 { + capability := aiCaps[len(aiCaps)-1] + price := n.GetBasePriceForCap("default", capability, config.ModelID) + glog.V(6).Infof("Capability %s (ID: %v) advertised with model constraint %s at price %d per %d unit", config.Pipeline, capability, config.ModelID, price.Num(), price.Denom()) + } } } From 93caa3bf73c2d2e59257f3d023a2a5d27f41bc54 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Wed, 24 Apr 2024 13:35:20 +0200 Subject: [PATCH 096/203] feat(ai): enforce 'aiModels' flag requirement (#3032) This commit ensures that an error is thrown when users don't specify the 'aiModels' flag but have the 'aiWorker' flag set. --- cmd/livepeer/starter/starter.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index a149ce62e..e612732a0 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -534,7 +534,7 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { if *cfg.AIModels != "" { configs, err := core.ParseAIModelConfigs(*cfg.AIModels) if err != nil { - glog.Error("Error parsing -aiModels: %v", err) + glog.Errorf("Error parsing -aiModels: %v", err) return } @@ -601,6 +601,9 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { glog.V(6).Infof("Capability %s (ID: %v) advertised with model constraint %s at price %d per %d unit", config.Pipeline, capability, config.ModelID, price.Num(), price.Denom()) } } + } else { + glog.Error("The '-aiModels' flag was set, but no model configuration was provided. Please specify the model configuration using the '-aiModels' flag.") + return } defer func() { From bbda63390dc76d3fd8503b3fc970e7ea1484c035 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 2 May 2024 22:39:43 +0200 Subject: [PATCH 097/203] fix(ai): improve AI selection algorithm (#3030) * fix(ai): improve selection algorithm This commit modifies the selection algorithm to continue retrying for a duration of one second instead of stopping after four attempts. This change addresses issues encountered with the current algorithm's performance in environments with 15 nodes on the network, ensuring more robust and reliable operation until further optimizations can be implemented. * refactor(ai): enhance selection algorithm retry logic This commit replaces the time-based for-loop in the selection algorithm's retry logic with a more context-aware approach. --- server/ai_process.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/server/ai_process.go b/server/ai_process.go index 54092c737..ba2cd6d5c 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -12,6 +12,7 @@ import ( "net/http" "path/filepath" "strings" + "time" "github.com/livepeer/ai-worker/worker" "github.com/livepeer/go-livepeer/clog" @@ -21,7 +22,7 @@ import ( "github.com/livepeer/lpms/stream" ) -const maxProcessingRetries = 4 +const processingRetryTimeout = 2 * time.Second const defaultTextToImageModelID = "stabilityai/sdxl-turbo" const defaultImageToImageModelID = "stabilityai/sdxl-turbo" const defaultImageToVideoModelID = "stabilityai/stable-video-diffusion-img2vid-xt" @@ -311,8 +312,11 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface var resp *worker.ImageResponse + cctx, cancel := context.WithTimeout(context.Background(), processingRetryTimeout) + defer cancel() + tries := 0 - for tries < maxProcessingRetries { + for { tries++ sess, err := params.sessManager.Select(ctx, cap, modelID) @@ -334,6 +338,10 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface clog.Infof(ctx, "Error submitting request cap=%v modelID=%v try=%v orch=%v err=%v", cap, modelID, tries, sess.Transcoder(), err) params.sessManager.Remove(ctx, sess) + + if cctx.Err() == context.DeadlineExceeded { + break + } } if resp == nil { From 72dced7b710e407248f019117cee5e1777d00285 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Fri, 3 May 2024 23:26:07 +0200 Subject: [PATCH 098/203] refactor(ai): improve orch select retry ctx logic (#3039) This commit refines context handling in the orchestrator selection loop for idiomatic Go and enhanced propagation of parent cancellations. --- server/ai_process.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/server/ai_process.go b/server/ai_process.go index ba2cd6d5c..0355b1450 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -312,7 +312,7 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface var resp *worker.ImageResponse - cctx, cancel := context.WithTimeout(context.Background(), processingRetryTimeout) + cctx, cancel := context.WithTimeout(ctx, processingRetryTimeout) defer cancel() tries := 0 @@ -339,8 +339,10 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface params.sessManager.Remove(ctx, sess) - if cctx.Err() == context.DeadlineExceeded { - break + select { + case <-cctx.Done(): + return nil, &ServiceUnavailableError{err: errors.New("no orchestrators available: operation timed out")} + default: } } From 6fc1afd49af05d7515495a58ceeb60db206fe98c Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Fri, 3 May 2024 23:50:24 +0200 Subject: [PATCH 099/203] refactor(ai): improve orch retry timeout msg This commit improves the orchestrator selection retry ctx timeout msg. --- server/ai_process.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/ai_process.go b/server/ai_process.go index 0355b1450..a3c57244c 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -6,6 +6,7 @@ import ( "context" "encoding/json" "errors" + "fmt" "image" "io" "math/big" @@ -341,7 +342,7 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface select { case <-cctx.Done(): - return nil, &ServiceUnavailableError{err: errors.New("no orchestrators available: operation timed out")} + return nil, &ServiceUnavailableError{err: fmt.Errorf("no orchestrators available within %v timeout", processingRetryTimeout)} default: } } From a0448d4035391a270294c28db188660fa4575c9b Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Sat, 4 May 2024 00:34:07 +0200 Subject: [PATCH 100/203] fix(ai): prevent insufficient capacity payments (#3035) * fix(ai): handle insufficient capacity payments This commit enhances the Orchestrator's capacity handling by returning an error prior to processing payments when capacity is insufficient. This prevents that the Gateway overpays for requests. * chore(ai): update ai-worker dependency This commit updates the ai-worker dependency to the latest version. --- core/ai.go | 1 + core/orchestrator.go | 5 +++++ go.mod | 2 +- go.sum | 4 ++-- server/ai_http.go | 11 +++++++++++ server/rpc.go | 1 + 6 files changed, 21 insertions(+), 3 deletions(-) diff --git a/core/ai.go b/core/ai.go index 4a7a5ecd0..e87cec69a 100644 --- a/core/ai.go +++ b/core/ai.go @@ -17,6 +17,7 @@ type AI interface { ImageToVideo(context.Context, worker.ImageToVideoMultipartRequestBody) (*worker.VideoResponse, error) Warm(context.Context, string, string, worker.RunnerEndpoint, worker.OptimizationFlags) error Stop(context.Context) error + HasCapacity(pipeline, modelID string) bool } type AIModelConfig struct { diff --git a/core/orchestrator.go b/core/orchestrator.go index 4e7c6800d..bbef0e0dc 100644 --- a/core/orchestrator.go +++ b/core/orchestrator.go @@ -93,6 +93,11 @@ func (orch *orchestrator) CheckCapacity(mid ManifestID) error { return nil } +// CheckAICapacity verifies if the orchestrator can process a request for a specific pipeline and modelID. +func (orch *orchestrator) CheckAICapacity(pipeline, modelID string) bool { + return orch.node.AIWorker.HasCapacity(pipeline, modelID) +} + func (orch *orchestrator) TranscodeSeg(ctx context.Context, md *SegTranscodingMetadata, seg *stream.HLSSegment) (*TranscodeResult, error) { return orch.node.sendToTranscodeLoop(ctx, md, seg) } diff --git a/go.mod b/go.mod index 339f8805a..63c50f41c 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.0-20240416102817-db751cd47fdc + github.com/livepeer/ai-worker v0.0.0-20240503212430-b8dc520fb581 github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 diff --git a/go.sum b/go.sum index a19f36cfb..4635f1cd4 100644 --- a/go.sum +++ b/go.sum @@ -532,8 +532,8 @@ github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4n github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/livepeer/ai-worker v0.0.0-20240416102817-db751cd47fdc h1:wmXT+pkcAshQ5yYiDLpoeIY0+P3Ob4RxHOljbS13FfA= -github.com/livepeer/ai-worker v0.0.0-20240416102817-db751cd47fdc/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= +github.com/livepeer/ai-worker v0.0.0-20240503212430-b8dc520fb581 h1:PJLuYUl9N52foLyGq8lay/xSyF8v5FzLrJnVb2TG1Aw= +github.com/livepeer/ai-worker v0.0.0-20240503212430-b8dc520fb581/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= diff --git a/server/ai_http.go b/server/ai_http.go index d4af86a4d..bc07b6350 100644 --- a/server/ai_http.go +++ b/server/ai_http.go @@ -3,6 +3,7 @@ package server import ( "context" "encoding/json" + "fmt" "image" "net/http" "strconv" @@ -122,12 +123,14 @@ func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request } var cap core.Capability + var pipeline string var modelID string var submitFn func(context.Context) (*worker.ImageResponse, error) var outPixels int64 switch v := req.(type) { case worker.TextToImageJSONRequestBody: + pipeline = "text-to-image" cap = core.Capability_TextToImage modelID = *v.ModelId submitFn = func(ctx context.Context) (*worker.ImageResponse, error) { @@ -146,6 +149,7 @@ func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request outPixels = height * width case worker.ImageToImageMultipartRequestBody: + pipeline = "image-to-image" cap = core.Capability_ImageToImage modelID = *v.ModelId submitFn = func(ctx context.Context) (*worker.ImageResponse, error) { @@ -164,6 +168,7 @@ func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request } outPixels = int64(config.Height) * int64(config.Width) case worker.ImageToVideoMultipartRequestBody: + pipeline = "image-to-video" cap = core.Capability_ImageToVideo modelID = *v.ModelId submitFn = func(ctx context.Context) (*worker.ImageResponse, error) { @@ -194,6 +199,12 @@ func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request manifestID := core.ManifestID(strconv.Itoa(int(cap)) + "_" + modelID) + // Check if there is capacity for the request. + if !orch.CheckAICapacity(pipeline, modelID) { + respondWithError(w, fmt.Sprintf("Insufficient capacity for pipeline=%v modelID=%v", pipeline, modelID), http.StatusServiceUnavailable) + return + } + // Known limitation: // This call will set a fixed price for all requests in a session identified by a manifestID. // Since all requests for a capability + modelID are treated as "session" with a single manifestID, all diff --git a/server/rpc.go b/server/rpc.go index 29b22598b..ade60e49e 100644 --- a/server/rpc.go +++ b/server/rpc.go @@ -51,6 +51,7 @@ type Orchestrator interface { Sign([]byte) ([]byte, error) VerifySig(ethcommon.Address, string, []byte) bool CheckCapacity(core.ManifestID) error + CheckAICapacity(pipeline, modelID string) bool TranscodeSeg(context.Context, *core.SegTranscodingMetadata, *stream.HLSSegment) (*core.TranscodeResult, error) ServeTranscoder(stream net.Transcoder_RegisterTranscoderServer, capacity int, capabilities *net.Capabilities) TranscoderResults(job int64, res *core.RemoteTranscoderResult) From fb9764b7fdd978b691e5277e1a089b69a93793f8 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Sat, 4 May 2024 02:08:32 +0200 Subject: [PATCH 101/203] ci(ai): add temporary ai-video latest binary url upload This commit ensures that the `upload_build.sh` script uploads the latest binary that is deployed to the `ai-video` branch under one url. This is done to simplify binary installation. --- upload_build.sh | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/upload_build.sh b/upload_build.sh index 891dbd104..6cd4df884 100755 --- a/upload_build.sh +++ b/upload_build.sh @@ -114,6 +114,25 @@ stringToSign="PUT\n\n${contentType}\n${dateValue}\n${resource}" signature="$(echo -en ${stringToSign} | openssl sha1 -hmac ${GCLOUD_SECRET} -binary | base64)" fullUrl="https://storage.googleapis.com${resource}" +# Create temporary latest url for the 'ai-video' branch +# TODO: Once 'ai-video' is merged into the main branch, this section should be removed. +BRANCH="ai-video" +if [[ $BRANCH == "ai-video" ]]; then + BUCKET_PATH_LATEST="${PROJECT}/$BRANCH/latest/${FILE}" + resource_latest="/${BUCKET}/${BUCKET_PATH_LATEST}" + stringToSignLatest="PUT\n\n${contentType}\n${dateValue}\n${resource_latest}" + signatureLatest="$(echo -en ${stringToSignLatest} | openssl sha1 -hmac ${GCLOUD_SECRET} -binary | base64)" + fullUrlLatest="https://storage.googleapis.com${resource_latest}" + + # Upload the latest ai-video build + curl -X PUT -T "${FILE}" \ + -H "Host: storage.googleapis.com" \ + -H "Date: ${dateValue}" \ + -H "Content-Type: ${contentType}" \ + -H "Authorization: AWS ${GCLOUD_KEY}:${signatureLatest}" \ + $fullUrlLatest +fi + # Failsafe - don't overwrite existing uploads! if curl --head --fail $fullUrl 2>/dev/null; then echo "$fullUrl already exists, not overwriting!" From 40a40a53f5747ee76c7d7f471f18cfcddd7c0a96 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Sat, 4 May 2024 15:50:53 +0200 Subject: [PATCH 102/203] chore(ai): remove temporary AI subnet docs This commit removes the tempoary AI subnet docs now that the final docs have been deployed on https://docs.livepeer.ai/ai/introduction. --- doc/ai-subnet.md | 593 ----------------------------------------------- 1 file changed, 593 deletions(-) delete mode 100644 doc/ai-subnet.md diff --git a/doc/ai-subnet.md b/doc/ai-subnet.md deleted file mode 100644 index da3d64713..000000000 --- a/doc/ai-subnet.md +++ /dev/null @@ -1,593 +0,0 @@ -# AI Subnet Setup Guide - -Welcome to the [Livepeer Artificial Intelligence (AI) Subnet](https://explorer.livepeer.org/treasury/110409521297538895053642752647313688591695822800862508217133236436856613165807) 🤖! This comprehensive guide will assist you in setting up your Orchestrator or Gateway (formerly called Broadcaster) to utilize the AI Subnet of the [Livepeer protocol](https://livepeer.org/) for AI job processing. - -> [!CAUTION] -> Please note that the _AI Subnet_ is currently in **alpha** and under active development. Running it on the same machine as your main Orchestrator/Gateway node is not recommended due to potential stability issues. Proceed with caution and understand the associated risks. - -## Background - - - -## Terminology - -Before proceeding, ensure you understand these key terms: - -- **AI Subnet**: The [client software](https://github.com/livepeer/go-livepeer/tree/ai-video) implementing the AI functionality of the Livepeer protocol that can run AI Orchestration and Gateway nodes both **off-chain** and **on-chain**. -- **Mainnet AI Subnet**: The collective of Orchestrators and Gateways processing AI jobs on the Livepeer mainnet. -- **Mainnet Transcoding Network**: The primary Livepeer network handling video transcoding jobs. - -### Supported AI Models - -During the **alpha** and **beta** phases, the _AI Subnet_ supports a limited number of AI models per inference pipeline. The currently supported models per pipeline are: - -**Text-to-Image**: - -- [sd-turbo](https://huggingface.co/stabilityai/sd-turbo) -- [sdxl-turbo](https://huggingface.co/stabilityai/sdxl-turbo) -- [stable-diffusion-v1-5](https://huggingface.co/runwayml/stable-diffusion-v1-5) -- [stable-diffusion-xl-base-1.0](https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0) -- [openjourney-v4](https://huggingface.co/prompthero/openjourney-v4) -- [ByteDance/SDXL-Lightning](https://huggingface.co/ByteDance/SDXL-Lightning) - -**Image-to-Image**: - -- [sd-turbo](https://huggingface.co/stabilityai/sd-turbo) -- [sdxl-turbo](https://huggingface.co/stabilityai/sdxl-turbo) -- [ByteDance/SDXL-Lightning](https://huggingface.co/ByteDance/SDXL-Lightning) - -**Image-to-Video**: - -- [stable-video-diffusion-img2vid-xt](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt) -- [stabilityai/stable-video-diffusion-img2vid-xt-1-1](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt-1-1) - -When the _Mainnet AI Subnet_ is fully operational we plan to support any ([diffusion](https://huggingface.co/docs/diffusers/en/index)) model that can be run in a docker container. - -## Prerequisites - -Before starting with either the binary or Docker installation for the _Mainnet AI Subnet_, ensure your system meets these requirements: - -- [An Nvidia GPU](https://developer.nvidia.com/cuda-gpus) -- [The Nvidia driver](https://www.nvidia.com/Download/index.aspx) -- [Docker](https://docs.docker.com/install/) -- [Nvidia Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html) -- [Cuda 12](https://developer.nvidia.com/cuda-downloads) (only required for the binary installation) - -> [!NOTE] -> Although this guide was tested on Linux, experienced users can adapt the instructions for Windows or macOS environments. - -## Off-chain Setup - -For testing and development purposes, it's a good practice first to run the Orchestrator and Gateway nodes **off-chain**. This allows you to quickly test the _AI Subnet_ and ensure that your Orchestrator and Gateway nodes function correctly before connecting them to the **on-chain** [Livepeer protocol](https://livepeer.org/). - -### Orchestrator Setup - -#### AI Models Configuration - -##### How to Configure - -Orchestrators on the _AI Subnet_ can select the [supported models](#supported-ai-models) they wish to advertise and process. To do this: - -1. **AI Model Configuration**: Create an `aiModels.json` file in the `~/.lpData` directory to specify the AI models to support in the _AI Subnet_. Refer to the provided example below for proper formatting: - - ```json - [ - { - "pipeline": "text-to-image", - "model_id": "ByteDance/SDXL-Lightning", - "price_per_unit": 4768371, - "warm": true - }, - { - "pipeline": "image-to-image", - "model_id": "ByteDance/SDXL-Lightning", - "price_per_unit": 4768371 - }, - { - "pipeline": "image-to-video", - "model_id": "stabilityai/stable-video-diffusion-img2vid-xt-1-1", - "price_per_unit": 3390842, - "warm": true, - "optimization_flags": { - "SFAST": true - } - } - ] - ``` - - In this configuration: - - - `pipeline`: This mandatory field specifies the type of inference you want to run. The currently supported pipelines are `text-to-image`, `image-to-video`, and `image-to-image`. - - `model_id`: This mandatory field is the [Hugging Face model ID](https://huggingface.co/docs/transformers/en/main_classes/model) of the model you want to use. - - `price_per_unit`: This mandatory field is the price in [Wei](https://ethdocs.org/en/latest/ether.html) per unit of work. - - `warm`: By default, the Livepeer software dynamically loads the model onto the GPU as needed. However, if you set this flag to `true`, the model will be preloaded onto the GPU when the Orchestrator starts and will remain there, a state referred to as 'warm'. This approach reduces the model's runtime as it's already loaded onto the GPU when requests com in. Please note that in our current **alpha** phase, we only support one model per GPU. Consequently, if you have one GPU and one 'warm' model, you won't be able to serve any other models. - - `warm`: By default, the Livepeer software loads the AI model onto the GPU on-demand. If you set this flag to `true`, the model is preloaded onto the GPU when the Orchestrator starts, keeping it 'warm'. This means it stays loaded on the GPU, reducing the model's execution time as it's immediately available when requests come in. However, in our current **alpha** phase, we support only one model per GPU. So, if you have a single GPU and a 'warm' model, you won't be able to serve additional models. - - `optimization_flags`: This optional field allows you to enable specific optimizations for the pipeline (see [Optimization Flags](#optimization-flags)). - -2. **Install Hugging Face CLI**: Install the Hugging Face CLI by running the following command: - - ```bash - pip install huggingface_hub[cli,hf_transfer] - ``` - -3. **Create a Hugging Face Access Token**: Follow the instructions in the [Hugging Face documentation](https://huggingface.co/docs/hub/en/security-tokens) to create a Hugging Face access token and make it available under the `HG_TOKEN` environment variable. This token will download [token-gated models](https://huggingface.co/docs/transformers.js/en/guides/private) from the Hugging Face model hub. Alternatively, you can also install your Hugging Face access token on your machine using [login command](https://huggingface.co/docs/huggingface_hub/en/guides/cli#huggingface-cli-login) of the Hugging Face CLI. - - > [!IMPORTANT] - > The `ld_checkpoints.sh` script contains the [SVD1.1](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt-1-1) model, which currently also requires you to agree to the model's license agreement. If you want to advertise this model on the _AI Subnet_, you need to go to the [model page](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt-1-1), log in, and accept their terms. - -4. **Download AI Models**: Download the models listed in `aiModels.json` to the `~/.lpData/models` directory using the [ld_checkpoints.sh](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) script from the [livepeer/ai-worker](https://github.com/livepeer/ai-worker/blob/main/runner/dl_checkpoints.sh) repository. To do this you can run the following in your terminal from the `.lpData` directory: - - ```bash - curl -s https://raw.githubusercontent.com/livepeer/ai-worker/main/runner/dl_checkpoints.sh | bash -s -- --alpha - ``` - - > [!NOTE] - > The `--alpha` flag is used to download only the models currently supported by the Livepeer.inc Gateway node on the _AI Subnet_. You can remove this flag if you want to download all models and advertise them for other Gateway nodes. - -##### Optimization Flags - -> [!WARNING] -> The flags described below are experimental and may not function as anticipated. If you encounter any issues, please report them to the [go-livepeer](https://github.com/livepeer/go-livepeer/issues/new/choose) repository. - -The `optimization_flags` attribute in the `aiModels.json` file provides a way to activate specific performance enhancements for the pipeline. Currently, the following flags are available: - -- `SFAST`: Enables the [stable-fast](https://github.com/chengzeyi/stable-fast) optimization, enhancing inference performance. - - **Usage**: Add `"SFAST": true` to the `optimization_flags` section in the `aiModels.json` file. - - **Limitations**: Currently, the `SFAST` flag is only effective for `warm` models in the `image-to-video` pipeline. While it accelerates inference times by approximately 25%, it also extends the Node startup time. - -#### Orchestrator Binary Setup - -To run the _AI Subnet_ Orchestrator **off-chain** using the [pre-build binaries](https://discord.com/channels/423160867534929930/577736983036559360), follow these steps: - -1. **Download the Latest AI Subnet Binary**: Visit the [#🪛│builds Channel](https://discord.com/channels/423160867534929930/577736983036559360) on the [Livepeer community Discord](https://discord.com/channels/423160867534929930/577736983036559360). Find the latest message mentioning `Branch: ai-video` and your platform under `Platform:`. This message includes the newest _AI Subnet_ binaries for your system. -2. **Extract and Configure the Binary**: Once downloaded, extract the binary to a directory of your choice. -3. **Pull the Latest AI Runner docker image**: The Livepeer _AI Subnet_ uses a containerized workflow to run the AI models. You can download the latest AI Runner container by running the following command: - - ```bash - docker pull livepeer/ai-runner:latest - ``` - -4. **Start the Orchestrator**: Run the following command to initiate your _AI Subnet_ Orchestrator: - - ```bash - ./livepeer \ - -orchestrator \ - -transcoder \ - -serviceAddr 0.0.0.0:8936 \ - -v 6 \ - -nvidia "all" \ - -aiWorker \ - -aiModels ~/.lpData/aiModels.json \ - -aiModelsDir ~/.lpData/models - ``` - - While most of these flags are already used in the [Mainnet transcoding network](https://github.com/livepeer/go-livepeer) (documented in the [Livepeer documentation](https://docs.livepeer.org/references/go-livepeer/cli-reference)), the `-aiWorker`, `-aiModels`, and `-aiModelsDir` flags are new. They enable the _AI Subnet_ Orchestrator, define the location of your AI models configuration, and specify the directory where the models are stored on your machine, respectively. If the `aiModelsDir` flag is not set, the _AI Subnet_ Orchestrator will look for the models in the `~/.lpData//models` directory. - -5. **Verify Setup**: If your _AI Subnet_ Orchestrator is running correctly, you should see the following output: - - ```bash - I0405 22:03:17.427058 2655655 rpc.go:301] Connecting RPC to uri=https://0.0.0.0:8936 - I0405 22:03:17.430371 2655655 rpc.go:254] Received Ping request - ``` - -6. **Check Port Availability**: Confirm that the _AI Subnet_ Orchestrator node is operating on port `8936`. Unblock port `8936` on your machine and set up port forwarding on your router to make the Gateway node accessible from the internet. - -> [!NOTE] -> If no binaries are available for your system, you can build the [ai-video branch](https://github.com/livepeer/go-livepeer/tree/ai-video) of [go-livepeer](https://github.com/livepeer/go-livepeer) from source by following the instructions in the [Livepeer repository](https://docs.livepeer.org/orchestrators/guides/install-go-livepeer) or by reaching out to the Livepeer community on [Discord](https://discord.gg/livepeer). - -#### Orchestrator Docker Setup - - - -To run the _AI Subnet_ Orchestrator **off-chain** using Docker, follow these steps: - -1. **Pull the AI Subnet Docker Image**: Pull the latest _AI Subnet_ Docker image from the [Livepeer Docker Hub](https://hub.docker.com/r/livepeer/go-livepeer-ai) using the following command: - - ```bash - docker pull livepeer/go-livepeer:ai-video - ``` - -2. **Pull the Latest AI Runner docker image**: The Livepeer _AI Subnet_ uses a [containerized workflow](https://www.ibm.com/topics/containerization) to run the AI models. You can download the latest [AI Runner](https://hub.docker.com/r/livepeer/ai-runner) image by running the following command: - - ```bash - docker pull livepeer/ai-runner:latest - ``` - -3. **Run the AI Subnet Docker Image**: Execute the following command to start your _AI Subnet_ Orchestrator: - - ```bash - docker run \ - --name livepeer_ai_orchestrator \ - -v ~/.lpData/:/root/.lpData/ \ - -v /var/run/docker.sock:/var/run/docker.sock \ - --network host \ - --gpus all \ - livepeer/go-livepeer:ai-video \ - -orchestrator \ - -transcoder \ - -serviceAddr 0.0.0.0:8936 \ - -v 6 \ - -nvidia "all" \ - -aiWorker \ - -aiModels /root/.lpData/aiModels.json \ - -aiModelsDir ~/.lpData/models - ``` - - As outlined in the [Orchestrator Binary Setup](#orchestrator-binary-setup), the `-aiWorker`, `-aiModels`, and `-aiModelsDir` flags are unique to the _AI Subnet_ Orchestrator. The remaining flags are common to the [Mainnet transcoding network](https://github.com/livepeer/go-livepeer) as detailed in the [Livepeer documentation](https://docs.livepeer.org/references/go-livepeer/cli-reference). The AI-specific flags activate the _AI Subnet_ Orchestrator, specify the location of your AI models configuration file, and define the directory for model storage on your machine. If `aiModelsDir` is not set, the _AI Subnet_ Orchestrator defaults to the `~/.lpData//models` directory for model storage. - -4. **Verify Setup**: If your _AI Subnet_ Orchestrator is running correctly, you should see the following output: - - ```bash - I0405 22:03:17.427058 2655655 rpc.go:301] Connecting RPC to uri=https://0.0.0.0:8936 - I0405 22:03:17.430371 2655655 rpc.go:254] Received Ping request - ``` - -5. **Check Port Availability**: Confirm that the _AI Subnet_ Orchestrator node is operating on port `8936`. Unblock port `8936` on your machine and set up port forwarding on your router to make the Gateway node accessible from the internet. - -### Gateway Setup - -#### Gateway Binary Setup - -Gateway nodes on the _AI Subnet_ can be set up using the [pre-built binaries](https://discord.com/channels/423160867534929930/577736983036559360). Follow these steps to run the _AI Subnet_ Gateway node **off-chain**: - -1. **Download and extract the latest AI Subnet Binary**: Follow steps 1 and 2 from the [Orchestrator Binary Setup](#orchestrator-binary-setup) to download and extract the latest _AI Subnet_ binary for your system. -2. **Start the Gateway**: Execute the following command to start your _AI Subnet_ Gateway node: - - ```bash - ./livepeer \ - -datadir ~/.lpData2 \ - -broadcaster \ - -orchAddr \ - -httpAddr 0.0.0.0:8937 \ - -v 6 \ - -httpIngest - ``` - - The flags used here also apply to the [Mainnet transcoding network](https://github.com/livepeer/go-livepeer). To comprehensively understand these flags, consult the [Livepeer documentation](https://docs.livepeer.org/references/go-livepeer/cli-reference). Specifically, the `--orchAddr` and `--httpAddr` flags are crucial for routing the Gateway node to your local Orchestrator (i.e., `0.0.0.0:8936`) and facilitating **off-chain** communication between the Gateway and the Orchestrator. - -3. **Verify Setup**: If your _AI Subnet_ Gateway node is running correctly, you should see the following output: - - ```bash - I0405 22:08:48.667588 2669536 mediaserver.go:226] HTTP Server listening on http://0.0.0.0:8937 - I0405 22:08:48.667607 2669536 lpms.go:92] LPMS Server listening on rtmp://127.0.0.1:1935 - ``` - -4. **Check Port Availability**: Confirm that the _AI Subnet_ Gateway node is operating on port `8937`. Unblock port `8937` on your machine and set up port forwarding on your router to make the Gateway node accessible from the internet. - -#### Gateway Docker Setup - -1. **Pull the AI Subnet Docker Image**: Follow step 1 from the [Orchestrator Docker Setup](#orchestrator-docker-setup) to pull the latest _AI Subnet_ Docker image from the [Livepeer Docker Hub](https://hub.docker.com/r/livepeer/go-livepeer-ai). -2. **Run the AI Subnet Docker Image**: Execute the following command to start your _AI Subnet_ Gateway node: - - ```bash - docker run \ - --name livepeer_ai_gateway \ - -v ~/.lpData2/:/root/.lpData2 \ - -p 8937:8937 \ - --network host \ - livepeer/go-livepeer:ai-video \ - -datadir ~/.lpData2 \ - -broadcaster \ - -orchAddr \ - -httpAddr 0.0.0.0:8937 \ - -v 6 \ - -httpIngest - ``` - - As outlined in the [Gateway Binary Setup](#gateway-binary-setup), the flags are common to the [Mainnet transcoding network](https://github.com/livepeer/go-livepeer) and are documented in the [Livepeer documentation](https://docs.livepeer.org/references/go-livepeer/cli-reference). The `--orchAddr` and `--httpAddr` flags are essential for directing the Gateway node to your local Orchestrator and ensuring **off-chain** communication between the Gateway and the Orchestrator, respectively. - -3. **Verify Setup**: If your _AI Subnet_ Gateway node is running correctly, you should see the following output: - - ```bash - I0405 22:08:48.667588 2669536 mediaserver.go:226] HTTP Server listening on http://0.0.0.0:8937 - I0405 22:08:48.667607 2669536 lpms.go:92] LPMS Server listening on rtmp://127.0.0.1:1935 - ``` - -4. **Check Port Availability**: Confirm that the _AI Subnet_ Gateway node is operating on port `8937`. Unblock port `8937` on your machine and set up port forwarding on your router to make the Gateway node accessible from the internet. - -#### AI Job Submission - -> [!IMPORTANT] -> If you're using the `warm` flag in your `aiModels.json` ensure you have the right pipeline running on your Orchestrator before submitting a job. - -Submit an AI inference job for each of the supported pipelines to verify the correct functioning of your **off-chain** Gateway and Orchestrator nodes. - -#### Text-to-Image Inference Job - -To send a `text-to-image` inference job to the Gateway node and receive the result, follow these steps: - -1. **Job Submission**: Submit a job using the `curl` command: - - ```bash - curl -X POST 0.0.0.0:8937/text-to-image -d '{"prompt":"a dog","model_id":"ByteDance/SDXL-Lightning"}' - ``` - - The output should look like: - - ```bash - {"images":[{"seed":280278971,"url":"/stream/34937c31/dc88c7c9.png"}]} - ``` - -2. **Result Retrieval**: After job completion, you'll receive a JSON with a URL to download the result. Use `curl` to download: - - ```bash - curl -O 0.0.0.0:8937/stream/34937c31/dc88c7c9.png - ``` - -Congratulations! You've successfully set up your **off-chain** _AI Subnet_ Orchestrator and Gateway nodes to process `text-to-image` inference jobs. 🎉 You can repeat the process for the `image-to-video` and `image-to-image` pipelines described below to ensure the correct functioning of all the AI inference pipelines you did set up in your `aiModels.json`. - -#### Image-to-Image Inference Job - - - -To send an `image-to-image` inference job to the Gateway node and receive the result, follow these steps: - -1. **Job Submission**: Submit a job using the `curl` command: - - ```bash - curl -X POST 0.0.0.0:8937/image-to-image -F image=@ -F prompt="a dog" -F model_id="ByteDance/SDXL-Lightning" - ``` - - > [!NOTE] - > Substitute `` with the **local path** of the image you want to use for video generation. This command employs [curl](https://curl.se/docs/manpage.html)'s `-F` flag to upload the image file to the Gateway node. Refer to the [curl documentation](https://curl.se/docs/manpage.html) for more details. - -2. **Result Retrieval**: After job completion, you'll receive a JSON with a URL to download the result. Use `curl` to download: - - ```bash - curl -O 0.0.0.0:8937/stream/dffff04c/6f247287.png - ``` - -#### Image-to-Video Inference Job - - - -To send an `image-to-video` inference job to the Gateway node and receive the result, follow these steps: - -1. **Job Submission**: Submit a job using the `curl` command: - - ```bash - curl -X POST localhost:8937/image-to-video -F image=@ -F model_id=stabilityai/stable-video-diffusion-img2vid-xt-1-1 - ``` - - > [!NOTE] - > Substitute `` with the **local path** of the image you want to use for video generation. This command employs [curl](https://curl.se/docs/manpage.html)'s `-F` flag to upload the image file to the Gateway node. Refer to the [curl documentation](https://curl.se/docs/manpage.html) for more details. - - The output should look like: - - ```bash - {"images":[{"seed":3865866304,"url":"/stream/8794a01b/1f9bc7f2.mp4"}]} - ``` - -2. **Result Retrieval**: After job completion, you'll receive a JSON with a URL to download the result. Use `curl` to download: - - ```bash - curl -O localhost:8936/stream/8794a01b/1f9bc7f2.mp4 - ``` - -## On-chain Configuration - -After successful **off-chain** testing of your Orchestrator and Gateway nodes, you can proceed with the **on-chain** configuration. This involves connecting your nodes to the [Livepeer protocol](https://livepeer.org/) for AI job processing on the _AI Subnet_. The instructions below will guide you through the steps to configure your Orchestrator and Gateway nodes **on-chain**, without affecting your main nodes. - -### On-chain AI Subnet Orchestrator Configuration - -To redeem _Mainnet AI Subnet_ tickets **on-chain**, ensure your _Mainnet Transcoding Network_ Orchestrator is set up and ranked in the top 100. Refer to the [Livepeer Orchestrator Setup Documentation](https://docs.livepeer.org/orchestrators/guides/mainnet-transcoding-network) for setup steps. Once set up, configure your _Mainnet AI Subnet_ Orchestrator **on-chain**. Run a separate Orchestrator for the _AI Subnet_ to avoid affecting your main Orchestrator. This Orchestrator will handle AI jobs on the _Mainnet AI Subnet_. If your main Orchestrator is operational, there are two methods for **on-chain** AI ticket redemption: - -- **Method 1 (Recommended)**: Redeem the AI tickets **on-chain** on your _Mainnet AI Subnet_ Orchestrator, using the `-ethOrchAddr` to set your _Mainnet Transcoding Network_ Orchestrator as the tickets `recipient. -- **Method 2**: Set up a ticket redemption service using the `-redeemer` flag, and have your _Mainnet AI Subnet_ Orchestrator send the tickets to this service using the `redeemerAddr` flag. - -Below, you'll find comprehensive instructions for the first method. While a [second method is available](https://docs.livepeer.org/references/go-livepeer/cli-reference#onchain), it's generally not advised due to its requirement for configuring a ticket redemption service directly on your primary Orchestrator. This setup could impact the Orchestrator's performance, introducing extra latency and the possibility of other complications. - -#### Set Ticket Recipient - -The first and **recommended method** is to use the `ethOrchAddr` flag to set the Ethereum address of your _Mainnet Transcoding Network_ Orchestrator as the recipient of the AI tickets. This ensures that the AI tickets are redeemed **on-chain** by your _AI Subnet_ Orchestrator, while the AI rewards are sent to your _Mainnet Transcoding Network_ Orchestrator. Follow these steps to set up your _Mainnet AI Subnet_ Orchestrator using this method: - -1. Create a new Ethereum account for your _Mainnet AI Subnet_ Orchestrator. It is recommended that you use a separate account from your _Mainnet Transcoding Network_ Orchestrator for security reasons. -2. Fund the Ethereum account with enough ETH to cover the gas costs of redeeming the AI tickets **on-chain**. -3. Open port `8936` on your machine and set up port forwarding on your router to make the _Mainnet AI Subnet_ Orchestrator accessible from the internet. - -After you have completed these steps, you can start your _Mainnet AI Subnet_ Orchestrator using the binary or Docker image. - -#### Allow your AI Subnet Orchestrator to be Discovered - -To ensure that _AI Gateway_ nodes can discover your _AI Subnet_ Orchestrator, you need to write your service URI to the blockchain. This URI should be in the format `https://:`. To write your service URI to the blockchain, go to the machine that contains your _Mainnet AI Subnet_ Orchestrator wallet and follow these steps: - -1. Install the [Foundry](https://book.getfoundry.sh/getting-started/installation) smart contract development toolchain: - - ```bash - curl -L https://foundry.paradigm.xyz | bash - source /home//.bashrc - foundryup - ``` - -2. Invoke the `SetServiceURI` function on the [AIServiceRegistry](https://arbiscan.io/address/0x04C0b249740175999E5BF5c9ac1dA92431EF34C5) contract. Use the KeyStore file and password from your _Mainnet Transcoding Network_ Orchestrator wallet to execute this command: - - ```bash - cast send --keystore '' --password '' --rpc-url 0x04C0b249740175999E5BF5c9ac1dA92431EF34C5 "setServiceURI(string)" https://: - ``` - - If everything was successful, you should see the following output: - - ```bash - blockHash 0x214a65d2dffd1732e971bd3662dcb681663c2eb0c95a33c8918bab0a44e2d3ed - blockNumber 200370198 - contractAddress - cumulativeGasUsed 555214 - effectiveGasPrice 11048000 - ``` - -3. To check if your service URI was successfully written to the blockchain, call the `getServiceURI` function on the [AIServiceRegistry](https://arbiscan.io/address/0x5d31637eb0f442376053d5dea2347f663c4019dc) contract using the following command: - - ```bash - cast call --rpc-url 0x04C0b249740175999E5BF5c9ac1dA92431EF34C5 "getServiceURI(address)" | xxd -r -p - ``` - - If successful, you should see the following output: - - ```bash - https://: - ``` - -Congratulations! Your _Mainnet AI Subnet_ Orchestrator is now set up to be discovered by _AI Gateway_ nodes on the _AI Subnet_. 🚀 - -#### Binary Startup Command - -To start your _Mainnet AI Subnet_ Orchestrator using the [pre-built binaries](https://discord.com/channels/423160867534929930/577736983036559360), use the following command: - -```bash -./livepeer \ - -network arbitrum-one-mainnet \ - -ethUrl https://arb1.arbitrum.io/rpc \ - -orchestrator \ - -transcoder \ - -serviceAddr : \ - -v 6 \ - -nvidia "all" \ - -aiWorker \ - -aiModels ~/.lpData/aiModels.json \ - -aiModelsDir ~/.lpData/models \ - -pricePerUnit 70 \ - -ticketEV 2999999999999 \ - -ethAcctAddr \ - -ethOrchAddr -``` - -The `pricePerUnit` flag is disregarded by the _AI Subnet_ Orchestrator, as the pricing is determined by the [AI models configuration file](#ai-models-configuration). However, it's currently still necessary to initiate the Orchestrator. While the majority of the other flags in this command resemble those used when operating a [Mainnet transcoding Orchestrator](https://docs.livepeer.org/references/go-livepeer/cli-reference), there are two AI-specific flags to be aware of when setting up your _Mainnet AI Subnet_ Orchestrator **on-chain** using Docker: - -- `-ethAcctAddr`: This flag specifies the Ethereum address of your _Mainnet AI Subnet_ Orchestrator. -- `-ethOrchAddr`: This flag specifies the Ethereum address of your _Mainnet Transcoding Network_ Orchestrator. - -Setting these flags ensures that your _Mainnet AI Subnet_ Orchestrator is correctly configured to redeem AI tickets **on-chain**. 🎉 - -#### Docker Startup Command - -To start your _Mainnet AI Subnet_ Orchestrator using Docker, follow these steps: - -1. **Mount the AI Subnet Orchestrator Wallet**: Ensure your AI Subnet Orchestrator wallet is available in the `~/.lpData/arbitrum-one-mainnet/keystore` directory. -2. **Mount the AI Subnet Orchestrator Password File**: Ensure your AI Subnet Orchestrator password is available in the `~/.lpData/.eth_secret` file. -3. **Run the Docker Command**: Execute the following command to start your _Mainnet AI Subnet_ Orchestrator: - - ```bash - docker run \ - --name livepeer_ai_orchestrator \ - -v ~/.lpData/:/root/.lpData/ \ - -v /var/run/docker.sock:/var/run/docker.sock \ - --network host \ - --gpus all \ - livepeer/go-livepeer:ai-video \ - -network arbitrum-one-mainnet \ - -ethUrl https://arb1.arbitrum.io/rpc \ - -orchestrator \ - -transcoder \ - -serviceAddr : \ - -v 6 \ - -nvidia "all" \ - -aiWorker \ - -aiModels /root/.lpData/aiModels.json \ - -aiModelsDir ~/.lpData/models \ - -pricePerUnit 70 \ - -ticketEV 2999999999999 \ - -ethKeystorePath /root/.lpData/arbitrum-one-mainnet/keystore \ - -ethPassword /root/.lpData/.eth_secret \ - -ethAcctAddr \ - -ethOrchAddr - ``` - -The `pricePerUnit` flag is disregarded by the _AI Subnet_ Orchestrator, as the pricing is determined by the [AI models configuration file](#ai-models-configuration). However, it's currently still necessary to initiate the Orchestrator. While the majority of the other flags in this command resemble those used when operating a [Mainnet transcoding Orchestrator](https://docs.livepeer.org/references/go-livepeer/cli-reference), there are four AI-specific flags to be aware of when setting up your _Mainnet AI Subnet_ Orchestrator **on-chain** using Docker: - -- `-ethAcctAddr`: This flag specifies the Ethereum address of your _Mainnet AI Subnet_ Orchestrator. -- `-ethOrchAddr`: This flag specifies the Ethereum address of your _Mainnet Transcoding Network_ Orchestrator. - -Additionally since the _AI Subnet_ software using [Docker-out-of-Docker](http://tdongsi.github.io/blog/2017/04/23/docker-out-of-docker/) to spin up the [AI Runner](https://github.com/livepeer/ai-worker) containers, two additional docker-specific flags are crucial: - -- `--network host`: Enables communication between the Docker daemon inside the container and the [AI Runner](https://github.com/livepeer/ai-worker) containers for AI inference jobs. -- `--aiModelsDir`: Specifies the directory on your **host machine** where AI models are stored. The Docker daemon uses this path to mount the models in the AI Runner containers. - -Correctly setting these flags configures your _Mainnet AI Subnet_ Orchestrator to successfully redeem AI tickets **on-chain**. 🎉 - -### On-chain Gateway Setup - -> [!IMPORTANT] -> During the **alpha** phase, we're focusing our development efforts on the Livepeer.inc Gateway node for **on-chain** operations. While we plan to support additional **on-chain** Gateway nodes in the future, we currently don't offer setup documentation for them. - -## Provide Metrics to the AI SPE Team - -### Orchestrator Metrics - -> [!IMPORTANT] -> Currently, only _AI Subnet_ Orchestrator nodes running inside a Docker container can send metrics. Binary installation does not support this feature. - -To help the AI SPE team monitor the _AI Subnet_ performance, you can opt to send metrics from your _AI Subnet_ Orchestrator. These metrics aid in understanding network health and identifying potential issues. Follow these steps: - -1. **Install Promtail**: Install [Promtail](https://grafana.com/docs/loki/latest/send-data/promtail/installation/) on your Orchestrator node: - - ```bash - docker pull grafana/promtail:2.9.4 - ``` - -2. **Configure Promtail**: Create a Promtail folder at `/etc/promtail` and add a `ai_subnet_promtail.yml` configuration file in this directory: - - ```yaml - # Replace placeholders with your details - clients: - - url: https://YOUR_NAME_HERE:YOUR_PASSWORD_HERE@dca-loki.livepeer.fun/loki/api/v1/push - external_labels: - node_name: YOUR_SERVER_NAME_HERE - operator: YOUR_NAME_HERE - - scrape_configs: - - job_name: docker_logs - docker_sd_configs: - - host: unix:///var/run/docker.sock - refresh_interval: 5s - relabel_configs: - - source_labels: ["__meta_docker_container_name"] - regex: "/(.*)" - target_label: "container" - - source_labels: ["__meta_docker_container_log_stream"] - target_label: "logstream" - - source_labels: ["__meta_docker_container_label_logging_jobname"] - target_label: "job" - ``` - -3. **Name Your Server**: Replace `YOUR_SERVER_NAME_HERE` with a unique server name. -4. **Obtain Credentials**: Contact the AI SPE team on the [ai-video channel](https://discord.com/channels/423160867534929930/1187806216185974934) of the Livepeer community on [Discord](https://discord.gg/livepeer) for `YOUR_NAME_HERE`, `YOUR_PASSWORD`, and `YOUR_SERVER_NAME_HERE` values. Update these in the Promtail configuration file. -5. **Start Promtail**: Run the following command: - - ```bash - docker run \ - -d \ - --name=ai_subnet_promtail \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -v /etc/promtail/:/etc/promtail/ \ - --network=host \ - grafana/promtail:2.9.4 \ - -config.file=/etc/promtail/ai_subnet_promtail.yml - ``` - -6. **Verify Setup**: Confirm that Promtail is running correctly by checking the logs: - - ```bash - docker logs ai_subnet_promtail - ``` - - The output should look like: - - ```bash - level=info ts=2024-04-05T20:19:44.284132548Z caller=promtail.go:133 msg="Reloading configuration file" md5sum=3d2b13da03e519c5fdcbdc424436d38f - level=info ts=2024-04-05T20:19:44.284653164Z caller=server.go:322 http=[::]:80 grpc=[::]:9095 msg="server listening on addresses" - level=info ts=2024-04-05T20:19:44.28473552Z caller=main.go:174 msg="Starting Promtail" version="(version=2.9.4, branch=HEAD, revision=f599ebc535)" - level=warn ts=2024-04-05T20:19:44.284751581Z caller=promtail.go:263 msg="enable watchConfig" - level=info ts=2024-04-05T20:19:49.285276484Z caller=target_group.go:128 msg="added Docker target" containerID=2b8e274905d269f987f1a9ff889c7d82e3bcca3e31cc26084fe5662fb0f2d298 - level=info ts=2024-04-05T20:19:49.285319906Z caller=target_group.go:128 msg="added Docker target" containerID=e611b5dac9ced58bb553c5e42a415f7062c78e202f5a1baf38b223fc033cbb5c - ``` - -Thank you for contributing to the AI SPE team's efforts to optimize the _AI Subnet_! 🚀 - -### Gateway Metrics - -> [!IMPORTANT] -> During the alpha phase, we only support the Livepeer.inc Gateway node for on-chain Gateway operations to streamline our development process. We plan to extend support to other on-chain Gateway nodes in the future. Therefore, we currently do not provide documentation for sending Gateway node metrics. - -## Issues - -If you encounter any issues or have questions, feel free to reach out to us in the [ai-video channel](https://discord.com/channels/423160867534929930/1187806216185974934) of the Livepeer community on [Discord](https://discord.gg/livepeer). From ebd504561bb07477adb4cc35cf8e24d33ec33a21 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Sat, 4 May 2024 22:10:23 +0200 Subject: [PATCH 103/203] fix(ai): fix infinite loop when no Os are found (#3042) This commit prevents the orchestrator selection go routine from staying in a infinite loop when no Orchestrators can be found. --- server/ai_process.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/server/ai_process.go b/server/ai_process.go index a3c57244c..513851374 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -318,8 +318,13 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface tries := 0 for { - tries++ + select { + case <-cctx.Done(): + return nil, &ServiceUnavailableError{err: fmt.Errorf("no orchestrators available within %v timeout", processingRetryTimeout)} + default: + } + tries++ sess, err := params.sessManager.Select(ctx, cap, modelID) if err != nil { clog.Infof(ctx, "Error selecting session cap=%v modelID=%v err=%v", cap, modelID, err) @@ -339,12 +344,6 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface clog.Infof(ctx, "Error submitting request cap=%v modelID=%v try=%v orch=%v err=%v", cap, modelID, tries, sess.Transcoder(), err) params.sessManager.Remove(ctx, sess) - - select { - case <-cctx.Done(): - return nil, &ServiceUnavailableError{err: fmt.Errorf("no orchestrators available within %v timeout", processingRetryTimeout)} - default: - } } if resp == nil { From dfb43206ce343bbd1c5b0811af3d37a0005b9fbd Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Mon, 6 May 2024 15:56:08 +0200 Subject: [PATCH 104/203] feat(ai): Enhance orchestrator selection by incorporating latency (#3043) This commit introduces latency consideration into the orchestrator selection process, addressing two key issues. Firstly, it resolves a minor bug where the algorithm consistently selected known orchestrators due to a condition that never evaluated to true (see [this condition](https://github.com/livepeer/go-livepeer/blob/1239b4e56133003fe6a98a863cce6bdd6b5f2532/server/selection.go#L110)). Secondly, this change ensures that, once all orchestrators have been evaluated, the one with the fastest response time for a specific job is chosen. While the current method for calculating latency is somewhat basic, it sets the foundation for more sophisticated enhancements in the future. Co-authored-by: Brad P <0xb79orch@gmail.com> --- server/ai_process.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/server/ai_process.go b/server/ai_process.go index 513851374..f7604ae94 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -93,7 +93,9 @@ func submitTextToImage(ctx context.Context, params aiRequestParams, sess *AISess } defer completeBalanceUpdate(sess.BroadcastSession, balUpdate) + start := time.Now() resp, err := client.TextToImageWithResponse(ctx, req, setHeaders) + took := time.Since(start) if err != nil { return nil, err } @@ -108,6 +110,13 @@ func submitTextToImage(ctx context.Context, params aiRequestParams, sess *AISess balUpdate.Status = ReceivedChange } + // TODO: Refine this rough estimate in future iterations + numImages := 1 + if req.NumImagesPerPrompt != nil { + numImages = *req.NumImagesPerPrompt + } + sess.LatencyScore = took.Seconds() / float64(outPixels) / float64(numImages) + return resp.JSON200, nil } @@ -168,7 +177,9 @@ func submitImageToImage(ctx context.Context, params aiRequestParams, sess *AISes } defer completeBalanceUpdate(sess.BroadcastSession, balUpdate) + start := time.Now() resp, err := client.ImageToImageWithBodyWithResponse(ctx, mw.FormDataContentType(), &buf, setHeaders) + took := time.Since(start) if err != nil { return nil, err } @@ -183,6 +194,9 @@ func submitImageToImage(ctx context.Context, params aiRequestParams, sess *AISes balUpdate.Status = ReceivedChange } + // TODO: Refine this rough estimate in future iterations + sess.LatencyScore = took.Seconds() / float64(outPixels) + return resp.JSON200, nil } @@ -246,7 +260,9 @@ func submitImageToVideo(ctx context.Context, params aiRequestParams, sess *AISes } defer completeBalanceUpdate(sess.BroadcastSession, balUpdate) + start := time.Now() resp, err := client.ImageToVideoWithBody(ctx, mw.FormDataContentType(), &buf, setHeaders) + took := time.Since(start) if err != nil { return nil, err } @@ -271,6 +287,9 @@ func submitImageToVideo(ctx context.Context, params aiRequestParams, sess *AISes return nil, err } + // TODO: Refine this rough estimate in future iterations + sess.LatencyScore = took.Seconds() / float64(outPixels) + return &res, nil } From 45cf167b81fb524d03672fa0be872827c0811df2 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Mon, 13 May 2024 09:37:29 +0200 Subject: [PATCH 105/203] chore(ai): update 'ai-worker' dependency This commit updates the 'ai-worker' dependency to the latest commit. --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 63c50f41c..6db449d94 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.0-20240503212430-b8dc520fb581 + github.com/livepeer/ai-worker v0.0.0-20240513072428-fa2f0abb821c github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 diff --git a/go.sum b/go.sum index 4635f1cd4..93f6216f7 100644 --- a/go.sum +++ b/go.sum @@ -532,8 +532,8 @@ github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4n github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/livepeer/ai-worker v0.0.0-20240503212430-b8dc520fb581 h1:PJLuYUl9N52foLyGq8lay/xSyF8v5FzLrJnVb2TG1Aw= -github.com/livepeer/ai-worker v0.0.0-20240503212430-b8dc520fb581/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= +github.com/livepeer/ai-worker v0.0.0-20240513072428-fa2f0abb821c h1:SURuR5apFMDMp5ZHDmgVU6TZZKSpsS6yiFGOzkne3QI= +github.com/livepeer/ai-worker v0.0.0-20240513072428-fa2f0abb821c/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= From 180041d520f887402cc2c4775a0c9af9bc112e86 Mon Sep 17 00:00:00 2001 From: Elite Encoder Date: Mon, 13 May 2024 04:45:31 -0400 Subject: [PATCH 106/203] feat: add '-gateway' and deprecate '-broadcaster' (#3048) This commit adds the `gateway` flag and deprecates the `broadcaster` flag per core team decision (details: https://discord.com/channels/423160867534929930/1051963444598943784/1210356864643109004). --- cmd/livepeer/livepeer.go | 3 ++- cmd/livepeer/starter/starter.go | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/cmd/livepeer/livepeer.go b/cmd/livepeer/livepeer.go index 4b9f01ecf..c418ebd84 100755 --- a/cmd/livepeer/livepeer.go +++ b/cmd/livepeer/livepeer.go @@ -139,7 +139,8 @@ func parseLivepeerConfig() starter.LivepeerConfig { // Transcoding: cfg.Orchestrator = flag.Bool("orchestrator", *cfg.Orchestrator, "Set to true to be an orchestrator") cfg.Transcoder = flag.Bool("transcoder", *cfg.Transcoder, "Set to true to be a transcoder") - cfg.Broadcaster = flag.Bool("broadcaster", *cfg.Broadcaster, "Set to true to be a broadcaster") + cfg.Gateway = flag.Bool("gateway", *cfg.Broadcaster, "Set to true to be a gateway") + cfg.Broadcaster = flag.Bool("broadcaster", *cfg.Broadcaster, "Set to true to be a broadcaster (**Deprecated**, use -gateway)") cfg.OrchSecret = flag.String("orchSecret", *cfg.OrchSecret, "Shared secret with the orchestrator as a standalone transcoder or path to file") cfg.TranscodingOptions = flag.String("transcodingOptions", *cfg.TranscodingOptions, "Transcoding options for broadcast job, or path to json config") cfg.MaxAttempts = flag.Int("maxAttempts", *cfg.MaxAttempts, "Maximum transcode attempts") diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index e612732a0..de575e0d8 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -91,6 +91,7 @@ type LivepeerConfig struct { Orchestrator *bool Transcoder *bool AIWorker *bool + Gateway *bool Broadcaster *bool OrchSecret *string TranscodingOptions *string @@ -167,6 +168,7 @@ func DefaultLivepeerConfig() LivepeerConfig { defaultOrchestrator := false defaultTranscoder := false defaultBroadcaster := false + defaultGateway := false defaultOrchSecret := "" defaultTranscodingOptions := "P240p30fps16x9,P360p30fps16x9" defaultMaxAttempts := 3 @@ -255,6 +257,7 @@ func DefaultLivepeerConfig() LivepeerConfig { // Transcoding: Orchestrator: &defaultOrchestrator, Transcoder: &defaultTranscoder, + Gateway: &defaultGateway, Broadcaster: &defaultBroadcaster, OrchSecret: &defaultOrchSecret, TranscodingOptions: &defaultTranscodingOptions, @@ -630,6 +633,9 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { n.NodeType = core.TranscoderNode } else if *cfg.Broadcaster { n.NodeType = core.BroadcasterNode + glog.Warning("-broadcaster flag is deprecated and will be removed in a future release. Please use -gateway instead") + } else if *cfg.Gateway { + n.NodeType = core.BroadcasterNode } else if (cfg.Reward == nil || !*cfg.Reward) && !*cfg.InitializeRound { exit("No services enabled; must be at least one of -broadcaster, -transcoder, -orchestrator, -redeemer, -reward or -initializeRound") } @@ -1357,7 +1363,7 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { case core.OrchestratorNode: glog.Infof("***Livepeer Running in Orchestrator Mode***") case core.BroadcasterNode: - glog.Infof("***Livepeer Running in Broadcaster Mode***") + glog.Infof("***Livepeer Running in Gateway Mode***") glog.Infof("Video Ingest Endpoint - rtmp://%v", *cfg.RtmpAddr) case core.TranscoderNode: glog.Infof("**Liveepeer Running in Transcoder Mode***") From c18d32562344baf4ec2a13417d31c7dfcf707bf2 Mon Sep 17 00:00:00 2001 From: Elite Encoder Date: Mon, 13 May 2024 06:48:56 -0400 Subject: [PATCH 107/203] feat: remove -pricePerUnit requirement for -aiWorker flag (#3047) * Remove -pricePerUnit requirement for orchestrator with -AIWorker flag * refactor: add PricePerUnit comment This commit reintroduces the previously omitted comment for the PricePerUnit variable, improving code readability and maintainability. * refactor: simplify PricePerUnit flag check condition This commit simplifies the conditional check used to check if the `PricePerUnit` flag is needed. --------- Co-authored-by: Rick Staa --- cmd/livepeer/starter/starter.go | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index de575e0d8..2fa7ee78b 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -879,19 +879,17 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { if *cfg.Orchestrator { // Set price per pixel base info - if *cfg.PixelsPerUnit <= 0 { - // Can't divide by 0 - panic(fmt.Errorf("-pixelsPerUnit must be > 0, provided %d", *cfg.PixelsPerUnit)) - } - if cfg.PricePerUnit == nil { + if cfg.PricePerUnit == nil && !*cfg.AIWorker { // Prevent orchestrators from unknowingly providing free transcoding panic(fmt.Errorf("-pricePerUnit must be set")) + } else if cfg.PricePerUnit != nil { + if *cfg.PricePerUnit < 0 { + panic(fmt.Errorf("-pricePerUnit must be >= 0, provided %d", *cfg.PricePerUnit)) + } + + n.SetBasePrice("default", big.NewRat(int64(*cfg.PricePerUnit), int64(*cfg.PixelsPerUnit))) + glog.Infof("Price: %d wei for %d pixels\n ", *cfg.PricePerUnit, *cfg.PixelsPerUnit) } - if *cfg.PricePerUnit < 0 { - panic(fmt.Errorf("-pricePerUnit must be >= 0, provided %d", *cfg.PricePerUnit)) - } - n.SetBasePrice("default", big.NewRat(int64(*cfg.PricePerUnit), int64(*cfg.PixelsPerUnit))) - glog.Infof("Price: %d wei for %d pixels\n ", *cfg.PricePerUnit, *cfg.PixelsPerUnit) if *cfg.PricePerBroadcaster != "" { ppb := getBroadcasterPrices(*cfg.PricePerBroadcaster) From ee787ea0b7cdee5f5e2586cfcbde1a594f390b50 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Tue, 14 May 2024 13:15:54 +0200 Subject: [PATCH 108/203] perf(ai): update ai-worker to enable DEEPCACHE optimization This commit updates the https://github.com/livepeer/ai-worker to the latest version so that Orchestrators can enable the [DeepCache](https://github.com/horseee/DeepCache) optimization. This optimization will provide a 50% speedup for multi-step inference requests. --- go.mod | 2 +- go.sum | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 6db449d94..9bffb3766 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.0-20240513072428-fa2f0abb821c + github.com/livepeer/ai-worker v0.0.0-20240514111049-8bd32b0259ab github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 diff --git a/go.sum b/go.sum index 93f6216f7..d899671f7 100644 --- a/go.sum +++ b/go.sum @@ -532,8 +532,10 @@ github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4n github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/livepeer/ai-worker v0.0.0-20240513072428-fa2f0abb821c h1:SURuR5apFMDMp5ZHDmgVU6TZZKSpsS6yiFGOzkne3QI= -github.com/livepeer/ai-worker v0.0.0-20240513072428-fa2f0abb821c/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= +github.com/livepeer/ai-worker v0.0.0-20240513130850-3528b0285962 h1:LrZuNRNM2WuKhSR0d5DoZesEGcVBvxVXw3VWYmxR38Y= +github.com/livepeer/ai-worker v0.0.0-20240513130850-3528b0285962/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= +github.com/livepeer/ai-worker v0.0.0-20240514111049-8bd32b0259ab h1:RAC0KcDgPvtuDvY4msZ5rYjHuiLez81YjGPMA0QycF8= +github.com/livepeer/ai-worker v0.0.0-20240514111049-8bd32b0259ab/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= From d5f5e1c6c7d0a97bdcabe13880b1458b32c5d06c Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Wed, 15 May 2024 12:58:27 +0200 Subject: [PATCH 109/203] chore: fix Mockgen dependency error This commit ensures that the global https://pkg.go.dev/github.com/golang/mock/Mockgen package is correctly found when the binary is built using the makescript. --- Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index db8026968..54ca0c6e9 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,8 @@ SHELL=/bin/bash GO_BUILD_DIR?="./" +MOCKGEN=go run github.com/golang/mock/mockgen + all: net/lp_rpc.pb.go net/redeemer.pb.go net/redeemer_mock.pb.go core/test_segment.go livepeer livepeer_cli livepeer_router livepeer_bench net/lp_rpc.pb.go: net/lp_rpc.proto @@ -10,8 +12,8 @@ net/redeemer.pb.go: net/redeemer.proto protoc -I=. --go_out=. --go-grpc_out=. $^ net/redeemer_mock.pb.go net/redeemer_grpc_mock.pb.go: net/redeemer.pb.go net/redeemer_grpc.pb.go - @mockgen -source net/redeemer.pb.go -destination net/redeemer_mock.pb.go -package net - @mockgen -source net/redeemer_grpc.pb.go -destination net/redeemer_grpc_mock.pb.go -package net + @$(MOCKGEN) -source net/redeemer.pb.go -destination net/redeemer_mock.pb.go -package net + @$(MOCKGEN) -source net/redeemer_grpc.pb.go -destination net/redeemer_grpc_mock.pb.go -package net core/test_segment.go: core/test_segment.sh core/test_segment.go From 62acd4a8080471c8603ccf8e90f3c02b6b703128 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Wed, 15 May 2024 13:55:49 +0200 Subject: [PATCH 110/203] ci(ai): ensure docker builder is build and pushed --- .github/workflows/build.yaml | 2 +- .github/workflows/docker.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index c38626e83..022d397b8 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -217,7 +217,7 @@ jobs: id: match-tag with: text: ${{ github.ref_name }} - regex: '^(master|main|v[0-9]+\.\d+\.\d+)$' + regex: '^(master|main|ai-video|v[0-9]+\.\d+\.\d+)$' - name: Codesign and notarize binaries if: steps.match-tag.outputs.match != '' && matrix.target.GOOS == 'darwin' diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index a1ad605d7..6168c58a7 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -127,7 +127,7 @@ jobs: id: match-tag with: text: ${{ github.ref_name }} - regex: '^(main|master|v[0-9]+\.\d+\.\d+)$' + regex: '^(main|master|ai-video|v[0-9]+\.\d+\.\d+)$' - name: Get build tags id: build-tag From 816920a51e883ac1e690fc15b7837e2189aa130b Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Wed, 15 May 2024 20:28:11 +0200 Subject: [PATCH 111/203] feat(ai): enable NSFW safety filter (#3054) This commit enables the NSFW filter on the AI Subnet that has been implement at the runner side in https://github.com/livepeer/ai-worker/pull/76. BREAKING CHANGE: Depending on how dApps interact with the subnet this could be a breaking change given that we return an extra `nsfw` property. --- go.mod | 2 +- go.sum | 6 ++---- server/ai_process.go | 4 ++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 9bffb3766..47a48dcaf 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.0-20240514111049-8bd32b0259ab + github.com/livepeer/ai-worker v0.0.0-20240515094133-272ac74175bb github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 diff --git a/go.sum b/go.sum index d899671f7..d611073d5 100644 --- a/go.sum +++ b/go.sum @@ -532,10 +532,8 @@ github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4n github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/livepeer/ai-worker v0.0.0-20240513130850-3528b0285962 h1:LrZuNRNM2WuKhSR0d5DoZesEGcVBvxVXw3VWYmxR38Y= -github.com/livepeer/ai-worker v0.0.0-20240513130850-3528b0285962/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= -github.com/livepeer/ai-worker v0.0.0-20240514111049-8bd32b0259ab h1:RAC0KcDgPvtuDvY4msZ5rYjHuiLez81YjGPMA0QycF8= -github.com/livepeer/ai-worker v0.0.0-20240514111049-8bd32b0259ab/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= +github.com/livepeer/ai-worker v0.0.0-20240515094133-272ac74175bb h1:ss33ERQz7kkMKY4cOSv1lSqbL2LNrwAYq8sVcKMrlUo= +github.com/livepeer/ai-worker v0.0.0-20240515094133-272ac74175bb/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= diff --git a/server/ai_process.go b/server/ai_process.go index f7604ae94..d5cb0805e 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -63,7 +63,7 @@ func processTextToImage(ctx context.Context, params aiRequestParams, req worker. return nil, err } - newMedia[i] = worker.Media{Url: newUrl, Seed: media.Seed} + newMedia[i] = worker.Media{Nsfw: media.Nsfw, Url: newUrl, Seed: media.Seed} } resp.Images = newMedia @@ -141,7 +141,7 @@ func processImageToImage(ctx context.Context, params aiRequestParams, req worker return nil, err } - newMedia[i] = worker.Media{Url: newUrl, Seed: media.Seed} + newMedia[i] = worker.Media{Nsfw: media.Nsfw, Url: newUrl, Seed: media.Seed} } resp.Images = newMedia From fc51f04f922e96ce1e462433e4b1f5e93ebb584f Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Fri, 17 May 2024 09:25:18 +0200 Subject: [PATCH 112/203] chore(ai): update ai-worker version This commit updates the ai-worker so that the right go bindings are available and no nil errors are thrown. --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 47a48dcaf..33ef818d3 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.0-20240515094133-272ac74175bb + github.com/livepeer/ai-worker v0.0.0-20240516145001-e1e33a577f71 github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 diff --git a/go.sum b/go.sum index d611073d5..723823665 100644 --- a/go.sum +++ b/go.sum @@ -532,8 +532,8 @@ github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4n github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/livepeer/ai-worker v0.0.0-20240515094133-272ac74175bb h1:ss33ERQz7kkMKY4cOSv1lSqbL2LNrwAYq8sVcKMrlUo= -github.com/livepeer/ai-worker v0.0.0-20240515094133-272ac74175bb/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= +github.com/livepeer/ai-worker v0.0.0-20240516145001-e1e33a577f71 h1:0sb92GeK6/MEMNq0Lo5hHfKSuYAGxj8W243FHx6hGMo= +github.com/livepeer/ai-worker v0.0.0-20240516145001-e1e33a577f71/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= From f9aa0c124b2fd47c61855bf203e1cc05b7a5ca57 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Fri, 17 May 2024 11:02:02 +0200 Subject: [PATCH 113/203] ci(ai): ensure livepeer builder builds on AI version tags This commit ensures that the livepeer builder is triggered when AI-version tags are used (e.g., `v0.7.2-ai-video-1`). --- .github/workflows/docker.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 6168c58a7..829ea2135 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -127,7 +127,7 @@ jobs: id: match-tag with: text: ${{ github.ref_name }} - regex: '^(main|master|ai-video|v[0-9]+\.\d+\.\d+)$' + regex: '^(main|master|ai-video|v[0-9]+\.\d+\.\d+|v[0-9]+\.\d+\.\d+-ai-video-\d+)' - name: Get build tags id: build-tag From 9584c3cd9ae27bf254eb3116781495f4cc384373 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Fri, 17 May 2024 15:26:20 +0200 Subject: [PATCH 114/203] fix: apply runner nil error fix (#3058) This commit ensures that the ai-worker is up to date so that no `nil` pointer runtime error is thrown when the runner container returns a empty response. --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 33ef818d3..9004523a8 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.0-20240516145001-e1e33a577f71 + github.com/livepeer/ai-worker v0.0.1 github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 diff --git a/go.sum b/go.sum index 723823665..ef66f3a21 100644 --- a/go.sum +++ b/go.sum @@ -532,8 +532,8 @@ github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4n github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/livepeer/ai-worker v0.0.0-20240516145001-e1e33a577f71 h1:0sb92GeK6/MEMNq0Lo5hHfKSuYAGxj8W243FHx6hGMo= -github.com/livepeer/ai-worker v0.0.0-20240516145001-e1e33a577f71/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= +github.com/livepeer/ai-worker v0.0.1 h1:0/4cwE3iWM9T522f+L4otMvKjVmPbawhMrLdNiTH0hU= +github.com/livepeer/ai-worker v0.0.1/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= From add73684102861cb1227f9be0921050e0e0829de Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 16 May 2024 20:06:18 +0200 Subject: [PATCH 115/203] refactor(census): rename Broadcaster metrics to Gateway (#3055) * refactor(census): rename Broadcaster metrics to Gateway This commit renames the metrics related to Broadcaster to Gateway, following a team decision. More details can be found in the discussion here: [Team Discussion Link](.com/channels/423160867534929930/1051963444598943784/1210356864643109004). * chore: update pending changelog --- monitor/census.go | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/monitor/census.go b/monitor/census.go index 3381a92d7..f89d1a41a 100644 --- a/monitor/census.go +++ b/monitor/census.go @@ -306,8 +306,8 @@ func InitCensus(nodeType NodeType, version string) { census.mTicketValueSent = stats.Float64("ticket_value_sent", "TicketValueSent", "gwei") census.mTicketsSent = stats.Int64("tickets_sent", "TicketsSent", "tot") census.mPaymentCreateError = stats.Int64("payment_create_errors", "PaymentCreateError", "tot") - census.mDeposit = stats.Float64("broadcaster_deposit", "Current remaining deposit for the broadcaster node", "gwei") - census.mReserve = stats.Float64("broadcaster_reserve", "Current remaining reserve for the broadcaster node", "gwei") + census.mDeposit = stats.Float64("gateway_deposit", "Current remaining deposit for the gateway node", "gwei") + census.mReserve = stats.Float64("gateway_reserve", "Current remaining reserve for the gateway node", "gwei") // Metrics for receiving payments census.mTicketValueRecv = stats.Float64("ticket_value_recv", "TicketValueRecv", "gwei") @@ -690,17 +690,32 @@ func InitCensus(nodeType NodeType, version string) { TagKeys: baseTagsWithManifestID, Aggregation: view.Sum(), }, + { + Name: "gateway_deposit", + Measure: census.mDeposit, + Description: "Current remaining deposit for the gateway node", + TagKeys: baseTagsWithEthAddr, + Aggregation: view.LastValue(), + }, + { + Name: "gateway_reserve", + Measure: census.mReserve, + Description: "Current remaining reserve for the gateway node", + TagKeys: baseTagsWithEthAddr, + Aggregation: view.LastValue(), + }, + // TODO: Keep the old names for backwards compatibility, remove in the future { Name: "broadcaster_deposit", Measure: census.mDeposit, - Description: "Current remaining deposit for the broadcaster node", + Description: "Current remaining deposit for the gateway node", TagKeys: baseTagsWithEthAddr, Aggregation: view.LastValue(), }, { Name: "broadcaster_reserve", Measure: census.mReserve, - Description: "Current remaining reserve for the broadcaster node", + Description: "Current remaining reserve for the gateway node", TagKeys: baseTagsWithEthAddr, Aggregation: view.LastValue(), }, @@ -1533,7 +1548,7 @@ func PaymentCreateError(ctx context.Context) { } } -// Deposit records the current deposit for the broadcaster +// Deposit records the current deposit for the gateway func Deposit(sender string, deposit *big.Int) { if err := stats.RecordWithTags(census.ctx, []tag.Mutator{tag.Insert(census.kSender, sender)}, census.mDeposit.M(wei2gwei(deposit))); err != nil { From 35db3f265bacef18e2159fe7952c58c9ae4258b4 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Mon, 20 May 2024 13:01:17 +0200 Subject: [PATCH 116/203] refactor: add -pricePerGateway and deprecate -pricePerBroadcaster (#3061) This commit adds the `pricePerGateway` flag and deprecates the `pricePerBroadcaster` flag per core team decision (details: https://discord.com/channels/423160867534929930/1051963444598943784/1210356864643109004). --- cmd/livepeer/livepeer.go | 1 + cmd/livepeer/starter/starter.go | 82 +++++++++++++++++++++------- cmd/livepeer/starter/starter_test.go | 25 ++++++--- 3 files changed, 80 insertions(+), 28 deletions(-) diff --git a/cmd/livepeer/livepeer.go b/cmd/livepeer/livepeer.go index c418ebd84..03f8bf332 100755 --- a/cmd/livepeer/livepeer.go +++ b/cmd/livepeer/livepeer.go @@ -181,6 +181,7 @@ func parseLivepeerConfig() starter.LivepeerConfig { // Unit of pixels for both O's basePriceInfo and B's MaxBroadcastPrice cfg.PixelsPerUnit = flag.Int("pixelsPerUnit", *cfg.PixelsPerUnit, "Amount of pixels per unit. Set to '> 1' to have smaller price granularity than 1 wei / pixel") cfg.AutoAdjustPrice = flag.Bool("autoAdjustPrice", *cfg.AutoAdjustPrice, "Enable/disable automatic price adjustments based on the overhead for redeeming tickets") + cfg.PricePerGateway = flag.String("pricePerGateway", *cfg.PricePerGateway, `json list of price per gateway or path to json config file. Example: {"gateways":[{"ethaddress":"address1","priceperunit":1000,"pixelsperunit":1},{"ethaddress":"address2","priceperunit":1200,"pixelsperunit":1}]}`) cfg.PricePerBroadcaster = flag.String("pricePerBroadcaster", *cfg.PricePerBroadcaster, `json list of price per broadcaster or path to json config file. Example: {"broadcasters":[{"ethaddress":"address1","priceperunit":1000,"pixelsperunit":1},{"ethaddress":"address2","priceperunit":1200,"pixelsperunit":1}]}`) // Interval to poll for blocks cfg.BlockPollingInterval = flag.Int("blockPollingInterval", *cfg.BlockPollingInterval, "Interval in seconds at which different blockchain event services poll for blocks") diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index 2fa7ee78b..9272731a9 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -129,6 +129,7 @@ type LivepeerConfig struct { PricePerUnit *int PixelsPerUnit *int AutoAdjustPrice *bool + PricePerGateway *string PricePerBroadcaster *string BlockPollingInterval *int Redeemer *bool @@ -210,6 +211,7 @@ func DefaultLivepeerConfig() LivepeerConfig { defaultMaxPricePerUnit := 0 defaultPixelsPerUnit := 1 defaultAutoAdjustPrice := true + defaultPricePerGateway := "" defaultPricePerBroadcaster := "" defaultBlockPollingInterval := 5 defaultRedeemer := false @@ -300,6 +302,7 @@ func DefaultLivepeerConfig() LivepeerConfig { MaxPricePerUnit: &defaultMaxPricePerUnit, PixelsPerUnit: &defaultPixelsPerUnit, AutoAdjustPrice: &defaultAutoAdjustPrice, + PricePerGateway: &defaultPricePerGateway, PricePerBroadcaster: &defaultPricePerBroadcaster, BlockPollingInterval: &defaultBlockPollingInterval, Redeemer: &defaultRedeemer, @@ -892,12 +895,14 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { } if *cfg.PricePerBroadcaster != "" { - ppb := getBroadcasterPrices(*cfg.PricePerBroadcaster) - for _, p := range ppb { - price := big.NewRat(p.PricePerUnit, p.PixelsPerUnit) - n.SetBasePrice(p.EthAddress, price) - glog.Infof("Price: %v set for broadcaster %v", price.RatString(), p.EthAddress) - } + glog.Warning("-PricePerBroadcaster flag is deprecated and will be removed in a future release. Please use -PricePerGateway instead") + cfg.PricePerGateway = cfg.PricePerBroadcaster + } + gatewayPrices := getGatewayPrices(*cfg.PricePerGateway) + for _, p := range gatewayPrices { + price := big.NewRat(p.PricePerUnit, p.PixelsPerUnit) + n.SetBasePrice(p.EthAddress, price) + glog.Infof("Price: %v set for broadcaster %v", price.RatString(), p.EthAddress) } n.AutoSessionLimit = *cfg.MaxSessions == "auto" @@ -1564,29 +1569,68 @@ func checkOrStoreChainID(dbh *common.DB, chainID *big.Int) error { return nil } -// Format of broadcasterPrices json -// {"broadcasters":[{"ethaddress":"address1","priceperunit":1000,"pixelsperunit":1}, {"ethaddress":"address2","priceperunit":2000,"pixelsperunit":3}]} -type BroadcasterPrices struct { - Prices []BroadcasterPrice `json:"broadcasters"` -} - -type BroadcasterPrice struct { +type GatewayPrice struct { EthAddress string `json:"ethaddress"` PricePerUnit int64 `json:"priceperunit"` PixelsPerUnit int64 `json:"pixelsperunit"` } -func getBroadcasterPrices(broadcasterPrices string) []BroadcasterPrice { - var pricesSet BroadcasterPrices - prices, _ := common.ReadFromFile(broadcasterPrices) +func getGatewayPrices(gatewayPrices string) []GatewayPrice { + if gatewayPrices == "" { + return nil + } - err := json.Unmarshal([]byte(prices), &pricesSet) + // Format of gatewayPrices json + // {"gateways":[{"ethaddress":"address1","priceperunit":1000,"pixelsperunit":1}, {"ethaddress":"address2","priceperunit":2000,"pixelsperunit":3}]} + var pricesSet struct { + Gateways []struct { + EthAddress string `json:"ethaddress"` + PixelsPerUnit json.RawMessage `json:"pixelsperunit"` + PricePerUnit json.RawMessage `json:"priceperunit"` + } `json:"gateways"` + // TODO: Keep the old name for backwards compatibility, remove in the future + Broadcasters []struct { + EthAddress string `json:"ethaddress"` + PixelsPerUnit json.RawMessage `json:"pixelsperunit"` + PricePerUnit json.RawMessage `json:"priceperunit"` + } `json:"broadcasters"` + } + pricesFileContent, _ := common.ReadFromFile(gatewayPrices) + + err := json.Unmarshal([]byte(pricesFileContent), &pricesSet) if err != nil { - glog.Errorf("broadcaster prices could not be parsed: %s", err) + glog.Errorf("gateway prices could not be parsed: %s", err) return nil } - return pricesSet.Prices + // Check if broadcasters field is used and display a warning + if len(pricesSet.Broadcasters) > 0 { + glog.Warning("The 'broadcaster' property in the 'pricePerGateway' config is deprecated and will be removed in a future release. Please use 'gateways' instead.") + } + + // Combine broadcasters and gateways into a single slice + allGateways := append(pricesSet.Broadcasters, pricesSet.Gateways...) + + prices := make([]GatewayPrice, len(allGateways)) + for i, p := range allGateways { + pixelsPerUnit, err := strconv.ParseInt(string(p.PixelsPerUnit), 10, 64) + if err != nil { + glog.Errorf("Pixels per unit could not be parsed for gateway %v. must be a valid number, provided %s", p.EthAddress, p.PixelsPerUnit) + continue + } + pricePerUnit, err := strconv.ParseInt(string(p.PricePerUnit), 10, 64) + if err != nil { + glog.Errorf("Price per unit could not be parsed for gateway %v. must be a valid number, provided %s", p.EthAddress, p.PricePerUnit) + continue + } + prices[i] = GatewayPrice{ + EthAddress: p.EthAddress, + PricePerUnit: pricePerUnit, + PixelsPerUnit: pixelsPerUnit, + } + } + + return prices } func createSelectionAlgorithm(cfg LivepeerConfig) (common.SelectionAlgorithm, error) { diff --git a/cmd/livepeer/starter/starter_test.go b/cmd/livepeer/starter/starter_test.go index 18a08633a..e72e64c0b 100644 --- a/cmd/livepeer/starter/starter_test.go +++ b/cmd/livepeer/starter/starter_test.go @@ -2,6 +2,7 @@ package starter import ( "errors" + "fmt" "math/big" "os" "path/filepath" @@ -87,19 +88,25 @@ func TestIsLocalURL(t *testing.T) { assert.False(isLocal) } -func TestParseGetBroadcasterPrices(t *testing.T) { +func TestParseGetGatewayPrices(t *testing.T) { assert := assert.New(t) - j := `{"broadcasters":[{"ethaddress":"0x0000000000000000000000000000000000000000","priceperunit":1000,"pixelsperunit":1}, {"ethaddress":"0x1000000000000000000000000000000000000000","priceperunit":2000,"pixelsperunit":3}]}` + // TODO: Keep checking old field for backwards compatibility, remove in future + jsonTemplate := `{"%s":[{"ethaddress":"0x0000000000000000000000000000000000000000","priceperunit":1000,"pixelsperunit":1}, {"ethaddress":"0x1000000000000000000000000000000000000000","priceperunit":2000,"pixelsperunit":3}]}` + testCases := []string{"gateways", "broadcasters"} - prices := getBroadcasterPrices(j) - assert.NotNil(prices) - assert.Equal(2, len(prices)) + for _, tc := range testCases { + jsonStr := fmt.Sprintf(jsonTemplate, tc) - price1 := big.NewRat(prices[0].PricePerUnit, prices[0].PixelsPerUnit) - price2 := big.NewRat(prices[1].PricePerUnit, prices[1].PixelsPerUnit) - assert.Equal(big.NewRat(1000, 1), price1) - assert.Equal(big.NewRat(2000, 3), price2) + prices := getGatewayPrices(jsonStr) + assert.NotNil(prices) + assert.Equal(2, len(prices)) + + price1 := big.NewRat(prices[0].PricePerUnit, prices[0].PixelsPerUnit) + price2 := big.NewRat(prices[1].PricePerUnit, prices[1].PixelsPerUnit) + assert.Equal(big.NewRat(1000, 1), price1) + assert.Equal(big.NewRat(2000, 3), price2) + } } // Address provided to keystore file From 55185db1f41b20bc532cc1c7ea586d74ec1ceaa8 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Mon, 20 May 2024 13:45:47 +0200 Subject: [PATCH 117/203] ci: Protect Docker 'stable' tag This commit introduces a safeguard to ensure that the Docker image tagged as 'stable' is only pushed when a new tag is created on the stable branch. This prevents unintended updates to the stable Docker image, ensuring consistency and reliability for users relying on the stable tag. --- .github/workflows/docker.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 829ea2135..9cc0be4e6 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -82,7 +82,7 @@ jobs: type=semver,pattern={{major}}.{{minor}},prefix=v type=raw,value=latest,enable={{is_default_branch}} type=raw,value=${{ github.event.pull_request.head.ref }} - type=raw,value=stable,enable=${{ startsWith(github.event.ref, 'refs/tags/v') }} + type=raw,value=stable,enable=${{ startsWith(github.event.ref, 'refs/tags/v') && is_default_branch }} - name: Build and push livepeer docker image uses: docker/build-push-action@v5 From 2b8478d7c8cf9d64427732f55ad83b6c0e3c5794 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Mon, 20 May 2024 13:51:14 +0200 Subject: [PATCH 118/203] ci: fix syntax error in Docker action tags This commit addresses a syntax error in the Docker image tag creation step. --- .github/workflows/docker.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 9cc0be4e6..025ac0cc5 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -82,7 +82,7 @@ jobs: type=semver,pattern={{major}}.{{minor}},prefix=v type=raw,value=latest,enable={{is_default_branch}} type=raw,value=${{ github.event.pull_request.head.ref }} - type=raw,value=stable,enable=${{ startsWith(github.event.ref, 'refs/tags/v') && is_default_branch }} + type=raw,value=stable,enable=${{ startsWith(github.event.ref, 'refs/tags/v') && github.event.base_ref == 'refs/heads/main' }} - name: Build and push livepeer docker image uses: docker/build-push-action@v5 From 03b64fb4dc621fce1921581ef5e6df975c54b91b Mon Sep 17 00:00:00 2001 From: Elite Encoder Date: Mon, 20 May 2024 16:03:32 -0400 Subject: [PATCH 119/203] fix(ai): fix runtime error in aiWorker when pricePerUnit is unset (#3059) * Fix nil baseprice when pricePerUnit is unused in aiWorker * fix: fix priceInfo 'nil' error on discovery This commit ensures that when the `transcodePrice` is not set by the AI orchestrator no `nil` error is thrown when a Gateway requests the orchestrators OrchInfo. * fix(ai): fix incorrect transcodePrice condition This commit fixes the check that is performed to check if transcodePrice is set. --------- Co-authored-by: Rick Staa --- core/orchestrator.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/orchestrator.go b/core/orchestrator.go index bbef0e0dc..c183d335e 100644 --- a/core/orchestrator.go +++ b/core/orchestrator.go @@ -332,12 +332,13 @@ func (orch *orchestrator) priceInfo(sender ethcommon.Address, manifestID Manifes transcodePrice = orch.node.GetBasePrice("default") } - var basePrice *big.Rat + basePrice := big.NewRat(0, 1) if caps == nil { - basePrice = transcodePrice + if transcodePrice != nil { + basePrice = transcodePrice + } } else { // The base price is the sum of the prices of individual capability + model ID pairs - basePrice = big.NewRat(0, 1) for cap := range caps.Capacities { // If the capability does not have constraints (and thus any model constraints) skip it // because we only price a capability together with a model ID right now @@ -359,7 +360,7 @@ func (orch *orchestrator) priceInfo(sender ethcommon.Address, manifestID Manifes // If no priced capabilities were signaled by the broadcaster assume that they are requesting // transcoding and set the base price to the transcode price - if basePrice.Cmp(big.NewRat(0, 1)) == 0 { + if transcodePrice != nil && basePrice.Cmp(big.NewRat(0, 1)) == 0 { basePrice = transcodePrice } } From b7a9fb72f8d98eef5164a5c7e8b6820c9e9370bc Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Tue, 21 May 2024 00:54:13 +0200 Subject: [PATCH 120/203] fix(ai): fix cli prices nil error (#3063) This commit ensures that the livepeer_cli does not throw a `nil` error when it tries to retrieve the orchestrator base price. --- cmd/livepeer_cli/wizard_stats.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/livepeer_cli/wizard_stats.go b/cmd/livepeer_cli/wizard_stats.go index 8a251fc9a..c7049e012 100644 --- a/cmd/livepeer_cli/wizard_stats.go +++ b/cmd/livepeer_cli/wizard_stats.go @@ -219,6 +219,10 @@ func (w *wizard) orchestratorStats() { fmt.Println("+------------------+") table := tablewriter.NewWriter(os.Stdout) + basePrice := "n/a" + if priceInfo != nil { + basePrice = fmt.Sprintf("%v wei / %v pixels", priceInfo.Num(), priceInfo.Denom()) + } data := [][]string{ {"Status", t.Status}, {"Active", strconv.FormatBool(t.Active)}, @@ -227,7 +231,7 @@ func (w *wizard) orchestratorStats() { {"Reward Cut (%)", eth.FormatPerc(t.RewardCut)}, {"Fee Cut (%)", eth.FormatPerc(flipPerc(t.FeeShare))}, {"Last Reward Round", t.LastRewardRound.String()}, - {"Base price per pixel", fmt.Sprintf("%v wei / %v pixels", priceInfo.Num(), priceInfo.Denom())}, + {"Base price per pixel", basePrice}, {"Base price for broadcasters", b_prices}, } From 6843edab836c3ff9c7337bebdd76bc6b957841a1 Mon Sep 17 00:00:00 2001 From: Elite Encoder Date: Tue, 21 May 2024 04:34:24 -0400 Subject: [PATCH 121/203] feat: add -aiRunnerImage flag to pin docker image ver (#3064) This commit allows orchestrators to pin the https://hub.docker.com/r/livepeer/ai-runner image, preventing disruptions from breaking changes in the latest tag. --- cmd/livepeer/livepeer.go | 1 + cmd/livepeer/starter/starter.go | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/cmd/livepeer/livepeer.go b/cmd/livepeer/livepeer.go index 03f8bf332..99c625985 100755 --- a/cmd/livepeer/livepeer.go +++ b/cmd/livepeer/livepeer.go @@ -154,6 +154,7 @@ func parseLivepeerConfig() starter.LivepeerConfig { cfg.AIWorker = flag.Bool("aiWorker", *cfg.AIWorker, "Set to true to run an AI worker") cfg.AIModels = flag.String("aiModels", *cfg.AIModels, "Set models (pipeline:model_id) for AI worker to load upon initialization") cfg.AIModelsDir = flag.String("aiModelsDir", *cfg.AIModelsDir, "Set directory where AI model weights are stored") + cfg.AIRunnerImage = flag.String("aiRunnerImage", *cfg.AIRunnerImage, "Set the docker image for the AI runner: Example - livepeer/ai-runner:0.0.1") // Onchain: cfg.EthAcctAddr = flag.String("ethAcctAddr", *cfg.EthAcctAddr, "Existing Eth account address. For use when multiple ETH accounts exist in the keystore directory") diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index 9272731a9..f173bed6b 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -61,7 +61,6 @@ var ( // The time to live for cached max float values for PM senders (else they will be cleaned up) in seconds smTTL = 172800 // 2 days - aiWorkerContainerImageID = "livepeer/ai-runner:latest" aiWorkerContainerStopTimeout = 5 * time.Second ) @@ -151,6 +150,7 @@ type LivepeerConfig struct { OrchWebhookURL *string OrchBlacklist *string TestOrchAvail *bool + AIRunnerImage *string } // DefaultLivepeerConfig creates LivepeerConfig exactly the same as when no flags are passed to the livepeer process. @@ -190,6 +190,7 @@ func DefaultLivepeerConfig() LivepeerConfig { defaultAIWorker := false defaultAIModels := "" defaultAIModelsDir := "" + defaultAIRunnerImage := "livepeer/ai-runner:latest" // Onchain: defaultEthAcctAddr := "" @@ -278,9 +279,10 @@ func DefaultLivepeerConfig() LivepeerConfig { TestTranscoder: &defaultTestTranscoder, // AI: - AIWorker: &defaultAIWorker, - AIModels: &defaultAIModels, - AIModelsDir: &defaultAIModelsDir, + AIWorker: &defaultAIWorker, + AIModels: &defaultAIModels, + AIModelsDir: &defaultAIModelsDir, + AIRunnerImage: &defaultAIRunnerImage, // Onchain: EthAcctAddr: &defaultEthAcctAddr, @@ -531,7 +533,7 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { return } - n.AIWorker, err = worker.NewWorker(aiWorkerContainerImageID, gpus, modelsDir) + n.AIWorker, err = worker.NewWorker(*cfg.AIRunnerImage, gpus, modelsDir) if err != nil { glog.Errorf("Error starting AI worker: %v", err) return From 4382fbe6628c507c19333ba6583224fdb8aca3de Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Tue, 21 May 2024 10:36:39 +0200 Subject: [PATCH 122/203] chore(ai): update ai-worker dependency This commit updates the https://github.com/livepeer/ai-worker to the latest commit. --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9004523a8..1e9d2b128 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.1 + github.com/livepeer/ai-worker v0.0.2 github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 diff --git a/go.sum b/go.sum index ef66f3a21..1352de722 100644 --- a/go.sum +++ b/go.sum @@ -532,8 +532,8 @@ github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4n github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/livepeer/ai-worker v0.0.1 h1:0/4cwE3iWM9T522f+L4otMvKjVmPbawhMrLdNiTH0hU= -github.com/livepeer/ai-worker v0.0.1/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= +github.com/livepeer/ai-worker v0.0.2 h1:Tb9RlrQy+t2Dx0SVoxuSAqeOhRE8ONAHgnKiHyo31Ss= +github.com/livepeer/ai-worker v0.0.2/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= From 570a00b134e03ee33821922a7178c95408cb7245 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Wed, 29 May 2024 16:49:31 +0200 Subject: [PATCH 123/203] ci(docker): ensure stable tag is created on master branch This commit ensures that the stable tag is created on the master branch. --- .github/workflows/docker.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 025ac0cc5..7f6928ae6 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -82,7 +82,7 @@ jobs: type=semver,pattern={{major}}.{{minor}},prefix=v type=raw,value=latest,enable={{is_default_branch}} type=raw,value=${{ github.event.pull_request.head.ref }} - type=raw,value=stable,enable=${{ startsWith(github.event.ref, 'refs/tags/v') && github.event.base_ref == 'refs/heads/main' }} + type=raw,value=stable,enable=${{ startsWith(github.event.ref, 'refs/tags/v') && github.event.base_ref == 'refs/heads/master' }} - name: Build and push livepeer docker image uses: docker/build-push-action@v5 From aa8ae45f7ee8f980d9a76fb0754dd3a8c037d7d3 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Fri, 31 May 2024 13:45:57 +0200 Subject: [PATCH 124/203] feat: ai video add safety check to image to video 2 (#3071) * add safety check to image-to-video input image * refactor(ai): improve code syntax This commit improves the code syntax by making the output format generation step consistent between pipelines. It also updates the ai-worker to the latest version. --------- Co-authored-by: Brad P <0xb79orch@gmail.com> --- core/orchestrator.go | 2 ++ go.mod | 2 +- go.sum | 4 ++-- server/ai_process.go | 8 +++++--- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/core/orchestrator.go b/core/orchestrator.go index c183d335e..1a07a0ba4 100644 --- a/core/orchestrator.go +++ b/core/orchestrator.go @@ -1007,7 +1007,9 @@ func (n *LivepeerNode) imageToVideo(ctx context.Context, req worker.ImageToVideo Url: uri, } + // NOTE: Seed is consistent for video; NSFW check applies to first frame only. if len(batch) > 0 { + videos[i].Nsfw = batch[0].Nsfw videos[i].Seed = batch[0].Seed } } diff --git a/go.mod b/go.mod index 1e9d2b128..b7419c353 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.2 + github.com/livepeer/ai-worker v0.0.3 github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 diff --git a/go.sum b/go.sum index 1352de722..f47c97c7c 100644 --- a/go.sum +++ b/go.sum @@ -532,8 +532,8 @@ github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4n github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/livepeer/ai-worker v0.0.2 h1:Tb9RlrQy+t2Dx0SVoxuSAqeOhRE8ONAHgnKiHyo31Ss= -github.com/livepeer/ai-worker v0.0.2/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= +github.com/livepeer/ai-worker v0.0.3 h1:dOoi6UmU5ZoGLCmOTECQ+wo7JCvLgN3mN+Hy5LHrWW0= +github.com/livepeer/ai-worker v0.0.3/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= diff --git a/server/ai_process.go b/server/ai_process.go index d5cb0805e..dd4302715 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -63,7 +63,7 @@ func processTextToImage(ctx context.Context, params aiRequestParams, req worker. return nil, err } - newMedia[i] = worker.Media{Nsfw: media.Nsfw, Url: newUrl, Seed: media.Seed} + newMedia[i] = worker.Media{Nsfw: media.Nsfw, Seed: media.Seed, Url: newUrl} } resp.Images = newMedia @@ -141,7 +141,7 @@ func processImageToImage(ctx context.Context, params aiRequestParams, req worker return nil, err } - newMedia[i] = worker.Media{Nsfw: media.Nsfw, Url: newUrl, Seed: media.Seed} + newMedia[i] = worker.Media{Nsfw: media.Nsfw, Seed: media.Seed, Url: newUrl} } resp.Images = newMedia @@ -221,9 +221,11 @@ func processImageToVideo(ctx context.Context, params aiRequestParams, req worker } videos[i] = worker.Media{ - Url: newUrl, + Nsfw: media.Nsfw, Seed: media.Seed, + Url: newUrl, } + } resp.Images = videos From 6159a842e34a38343357ca9f4bafa836841c21d9 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Tue, 4 Jun 2024 15:05:48 +0200 Subject: [PATCH 125/203] chore(ai): update ai-worker version This commit updates the ai-worker dependency to the latest version (i.e. v0.0.4). --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index b7419c353..d59534cc3 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.3 + github.com/livepeer/ai-worker v0.0.4 github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 From bd34a1ddd037fc4a659860b6a4b663777c276cee Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Tue, 4 Jun 2024 17:17:55 +0200 Subject: [PATCH 126/203] chore(ai): update ai-worker to v0.0.5 This commit updates the AI worker to v0.0.5 so that people can use the new I2I pix2pix model. --- go.mod | 33 ++++++++++++++------------- go.sum | 70 ++++++++++++++++++++++++++++------------------------------ 2 files changed, 50 insertions(+), 53 deletions(-) diff --git a/go.mod b/go.mod index d59534cc3..806589e86 100644 --- a/go.mod +++ b/go.mod @@ -6,13 +6,13 @@ require ( contrib.go.opencensus.io/exporter/prometheus v0.4.2 github.com/cenkalti/backoff v2.2.1+incompatible github.com/ethereum/go-ethereum v1.13.5 - github.com/getkin/kin-openapi v0.122.0 + github.com/getkin/kin-openapi v0.124.0 github.com/golang/glog v1.1.1 github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.4 + github.com/livepeer/ai-worker v0.0.5 github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 @@ -25,12 +25,12 @@ require ( github.com/peterbourgon/ff/v3 v3.4.0 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.14.0 - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.9.0 github.com/testcontainers/testcontainers-go v0.9.0 github.com/urfave/cli v1.22.12 go.opencensus.io v0.24.0 go.uber.org/goleak v1.3.0 - golang.org/x/net v0.19.0 + golang.org/x/net v0.25.0 google.golang.org/grpc v1.57.1 google.golang.org/protobuf v1.31.0 pgregory.net/rapid v1.1.0 @@ -60,7 +60,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/deckarep/golang-set/v2 v2.1.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect - github.com/deepmap/oapi-codegen/v2 v2.1.0 // indirect + github.com/deepmap/oapi-codegen/v2 v2.2.0 // indirect github.com/distribution/reference v0.5.0 // indirect github.com/dlclark/regexp2 v1.7.0 // indirect github.com/docker/cli v24.0.5+incompatible // indirect @@ -75,26 +75,25 @@ require ( github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect github.com/ghodss/yaml v1.0.0 // indirect - github.com/go-chi/chi/v5 v5.0.11 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect - github.com/go-openapi/jsonpointer v0.19.6 // indirect - github.com/go-openapi/swag v0.22.4 // indirect + github.com/go-openapi/jsonpointer v0.20.2 // indirect + github.com/go-openapi/swag v0.22.8 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect github.com/google/s2a-go v0.1.4 // indirect github.com/google/uuid v1.5.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect github.com/googleapis/gax-go/v2 v2.10.0 // indirect - github.com/gorilla/mux v1.8.0 // indirect + github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/holiman/uint256 v1.2.3 // indirect @@ -172,7 +171,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/status-im/keycard-go v0.2.0 // indirect - github.com/stretchr/objx v0.5.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/supranational/blst v0.3.11 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect @@ -184,14 +183,14 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/crypto v0.17.0 // indirect + golang.org/x/crypto v0.23.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/mod v0.12.0 // indirect + golang.org/x/mod v0.17.0 // indirect golang.org/x/oauth2 v0.8.0 // indirect - golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.13.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect + golang.org/x/tools v0.21.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.125.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index f47c97c7c..46713872d 100644 --- a/go.sum +++ b/go.sum @@ -154,8 +154,8 @@ github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= -github.com/deepmap/oapi-codegen/v2 v2.1.0 h1:I/NMVhJCtuvL9x+S2QzZKpSjGi33oDZwPRdemvOZWyQ= -github.com/deepmap/oapi-codegen/v2 v2.1.0/go.mod h1:R1wL226vc5VmCNJUvMyYr3hJMm5reyv25j952zAVXZ8= +github.com/deepmap/oapi-codegen/v2 v2.2.0 h1:FW4f7C0Xb6EaezBSB3GYw2QGwHD5ChDflG+3xSZBdvY= +github.com/deepmap/oapi-codegen/v2 v2.2.0/go.mod h1:L4zUv7ULYDtYSb/aYk/xO3OYcQU6BoU/0viULkbi2DE= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= @@ -204,14 +204,12 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= 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/getkin/kin-openapi v0.122.0 h1:WB9Jbl0Hp/T79/JF9xlSW5Kl9uYdk/AWD0yAd9HOM10= -github.com/getkin/kin-openapi v0.122.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw= +github.com/getkin/kin-openapi v0.124.0 h1:VSFNMB9C9rTKBnQ/fpyDU8ytMTr4dWI9QovSKj9kz/M= +github.com/getkin/kin-openapi v0.124.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= -github.com/go-chi/chi/v5 v5.0.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA= -github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= 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= @@ -234,11 +232,10 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= -github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= +github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= +github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicbw= +github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= @@ -315,8 +312,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= @@ -351,8 +348,8 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGa github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= @@ -532,8 +529,8 @@ github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4n github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/livepeer/ai-worker v0.0.3 h1:dOoi6UmU5ZoGLCmOTECQ+wo7JCvLgN3mN+Hy5LHrWW0= -github.com/livepeer/ai-worker v0.0.3/go.mod h1:JvUlcQktSgkEfzotuelfw9OpjGi2qZTW/3tWB/klb/c= +github.com/livepeer/ai-worker v0.0.5 h1:f7eJXjV5x1sH6vMv24nm5JcJHMmxahsYzlfHnXUx2LU= +github.com/livepeer/ai-worker v0.0.5/go.mod h1:w/3SKld/dkHHAMMLKGidr49K2/lBeRve0lovs+5BuZ8= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= @@ -732,8 +729,8 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= @@ -769,8 +766,9 @@ github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -780,8 +778,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= @@ -871,8 +869,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -907,8 +905,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -949,8 +947,8 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -973,8 +971,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1041,8 +1039,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1056,8 +1054,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1113,8 +1111,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= +golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 6f9426bc94b54fab2b7d8fa5d4c4548c5fbd5b0a Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Tue, 4 Jun 2024 17:46:10 +0200 Subject: [PATCH 127/203] chore(ai): update ai-worker to latest version This commit updates the ai-worker to the latest version (i.e. v0.0.6) in order to fix a syntax error that was introduced due to an upstream dependency in v0.0.4 and v0.0.5. --- go.mod | 3 ++- go.sum | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 806589e86..17278a5a3 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.5 + github.com/livepeer/ai-worker v0.0.6 github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 @@ -75,6 +75,7 @@ require ( github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect github.com/ghodss/yaml v1.0.0 // indirect + github.com/go-chi/chi/v5 v5.0.11 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-logr/logr v1.2.4 // indirect diff --git a/go.sum b/go.sum index 46713872d..1f5c5d0eb 100644 --- a/go.sum +++ b/go.sum @@ -210,6 +210,8 @@ github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/go-chi/chi/v5 v5.0.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA= +github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= 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= @@ -529,8 +531,8 @@ github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4n github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/livepeer/ai-worker v0.0.5 h1:f7eJXjV5x1sH6vMv24nm5JcJHMmxahsYzlfHnXUx2LU= -github.com/livepeer/ai-worker v0.0.5/go.mod h1:w/3SKld/dkHHAMMLKGidr49K2/lBeRve0lovs+5BuZ8= +github.com/livepeer/ai-worker v0.0.6 h1:tkWodeuPyekZJzrtLvI61Ux2dRaBm24hC7wYguB6B4Y= +github.com/livepeer/ai-worker v0.0.6/go.mod h1:w/3SKld/dkHHAMMLKGidr49K2/lBeRve0lovs+5BuZ8= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= From 6535844394ec91edc4687fbee0dfceb1d13a54cd Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Wed, 5 Jun 2024 13:41:22 +0200 Subject: [PATCH 128/203] feat(ai): account for num_inference_steps inT2I latency inference score calculation (#3074) * fix(ai): Fix accuracy of T2I latency score when num_inference_steps provided * refactor(ai): update numInferenceSteps default This commit ensures that the same numInferenceSteps default value is used as the one set in https://github.com/livepeer/ai-worker/blob/31fe460a45e1d9e908d3a1bdcfdd8822c3889214/runner/app/routes/text_to_image.py#L28. --------- Co-authored-by: Elite Encoder --- server/ai_process.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/server/ai_process.go b/server/ai_process.go index dd4302715..e851b8d82 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -110,12 +110,18 @@ func submitTextToImage(ctx context.Context, params aiRequestParams, sess *AISess balUpdate.Status = ReceivedChange } - // TODO: Refine this rough estimate in future iterations - numImages := 1 + // TODO: Refine this rough estimate in future iterations. + // TODO: Default values for the number of images and inference steps are currently hardcoded. + // These should be managed by the nethttpmiddleware. Refer to issue LIV-412 for more details. + numImages := float64(1) if req.NumImagesPerPrompt != nil { - numImages = *req.NumImagesPerPrompt + numImages = float64(*req.NumImagesPerPrompt) + } + numInferenceSteps := float64(50) + if req.NumInferenceSteps != nil { + numInferenceSteps = float64(*req.NumInferenceSteps) } - sess.LatencyScore = took.Seconds() / float64(outPixels) / float64(numImages) + sess.LatencyScore = took.Seconds() / float64(outPixels) / (numImages * numInferenceSteps) return resp.JSON200, nil } From 0d51a78194703a53f7342de774881a2b64efd2b2 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Mon, 10 Jun 2024 13:56:00 +0200 Subject: [PATCH 129/203] chore(ai): update to latest ai-worker This commit ensures that the go-livepeer ai-video branch uses the latest ai-worker dependeny (i.e. v0.0.7). --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 17278a5a3..5dd7d2c93 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.6 + github.com/livepeer/ai-worker v0.0.7 github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 @@ -75,7 +75,7 @@ require ( github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect github.com/ghodss/yaml v1.0.0 // indirect - github.com/go-chi/chi/v5 v5.0.11 // indirect + github.com/go-chi/chi/v5 v5.0.12 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-logr/logr v1.2.4 // indirect diff --git a/go.sum b/go.sum index 1f5c5d0eb..b6430a64b 100644 --- a/go.sum +++ b/go.sum @@ -210,8 +210,8 @@ github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= -github.com/go-chi/chi/v5 v5.0.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA= -github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s= +github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= 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= @@ -531,8 +531,8 @@ github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4n github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/livepeer/ai-worker v0.0.6 h1:tkWodeuPyekZJzrtLvI61Ux2dRaBm24hC7wYguB6B4Y= -github.com/livepeer/ai-worker v0.0.6/go.mod h1:w/3SKld/dkHHAMMLKGidr49K2/lBeRve0lovs+5BuZ8= +github.com/livepeer/ai-worker v0.0.7 h1:mctm5jswdlMLUjaHeMhv3u4QtOAZ75jJhxgUdlzY5dU= +github.com/livepeer/ai-worker v0.0.7/go.mod h1:Xlnb0nFG2VsGeMG9hZmReVQXeFt0Dv28ODiUT2ooyLE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= From b2933cf01d4517cb4080a21c765529307881f8db Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Mon, 10 Jun 2024 20:21:31 +0200 Subject: [PATCH 130/203] feat(ai): add upscaling pipeline (#3077) * add upscale image support using stabilityai/stable-diffusion-x4-upscaler model * fix(ai): fix ai-worker client bindings This commit ensures that the right golang client bindings response and request types are used. It also cleans up the codebase a bit. --------- Co-authored-by: Mike Zupper --- ai/file_worker.go | 19 +++++++ cmd/livepeer/starter/starter.go | 12 +++++ core/ai.go | 1 + core/capabilities.go | 3 ++ core/orchestrator.go | 8 +++ go.mod | 2 +- go.sum | 4 +- server/ai_http.go | 43 ++++++++++++++++ server/ai_mediaserver.go | 49 ++++++++++++++++++ server/ai_process.go | 90 +++++++++++++++++++++++++++++++++ server/rpc.go | 1 + 11 files changed, 229 insertions(+), 3 deletions(-) diff --git a/ai/file_worker.go b/ai/file_worker.go index 40593977d..e9eb85c64 100644 --- a/ai/file_worker.go +++ b/ai/file_worker.go @@ -74,6 +74,25 @@ func (w *FileWorker) ImageToVideo(ctx context.Context, req worker.ImageToVideoMu return &resp, nil } +func (w *FileWorker) Upscale(ctx context.Context, req worker.UpscaleMultipartRequestBody) (*worker.ImageResponse, error) { + fname, ok := w.files["upscale"] + if !ok { + return nil, errors.New("upscale response file not found") + } + + data, err := os.ReadFile(fname) + if err != nil { + return nil, err + } + + var resp worker.ImageResponse + if err := json.Unmarshal(data, &resp); err != nil { + return nil, err + } + + return &resp, nil +} + func (w *FileWorker) Warm(ctx context.Context, containerName, modelID string) error { return nil } diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index f173bed6b..15cca7f8c 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -601,6 +601,18 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { constraints[core.Capability_ImageToVideo].Models[config.ModelID] = modelConstraint n.SetBasePriceForCap("default", core.Capability_ImageToVideo, config.ModelID, big.NewRat(config.PricePerUnit, config.PixelsPerUnit)) + case "upscale": + _, ok := constraints[core.Capability_Upscale] + if !ok { + aiCaps = append(aiCaps, core.Capability_Upscale) + constraints[core.Capability_Upscale] = &core.Constraints{ + Models: make(map[string]*core.ModelConstraint), + } + } + + constraints[core.Capability_Upscale].Models[config.ModelID] = modelConstraint + + n.SetBasePriceForCap("default", core.Capability_Upscale, config.ModelID, big.NewRat(config.PricePerUnit, config.PixelsPerUnit)) } if len(aiCaps) > 0 { diff --git a/core/ai.go b/core/ai.go index e87cec69a..c4a7146ec 100644 --- a/core/ai.go +++ b/core/ai.go @@ -15,6 +15,7 @@ type AI interface { TextToImage(context.Context, worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) ImageToImage(context.Context, worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) ImageToVideo(context.Context, worker.ImageToVideoMultipartRequestBody) (*worker.VideoResponse, error) + Upscale(context.Context, worker.UpscaleMultipartRequestBody) (*worker.ImageResponse, error) Warm(context.Context, string, string, worker.RunnerEndpoint, worker.OptimizationFlags) error Stop(context.Context) error HasCapacity(pipeline, modelID string) bool diff --git a/core/capabilities.go b/core/capabilities.go index 600a7ff2c..240222ab2 100644 --- a/core/capabilities.go +++ b/core/capabilities.go @@ -70,6 +70,7 @@ const ( Capability_TextToImage Capability_ImageToImage Capability_ImageToVideo + Capability_Upscale ) var CapabilityNameLookup = map[Capability]string{ @@ -104,6 +105,7 @@ var CapabilityNameLookup = map[Capability]string{ Capability_TextToImage: "Text to image", Capability_ImageToImage: "Image to image", Capability_ImageToVideo: "Image to video", + Capability_Upscale: "Upscale", } var CapabilityTestLookup = map[Capability]CapabilityTest{ @@ -192,6 +194,7 @@ func OptionalCapabilities() []Capability { Capability_TextToImage, Capability_ImageToImage, Capability_ImageToVideo, + Capability_Upscale, } } diff --git a/core/orchestrator.go b/core/orchestrator.go index 1a07a0ba4..f9d42b3fa 100644 --- a/core/orchestrator.go +++ b/core/orchestrator.go @@ -122,6 +122,10 @@ func (orch *orchestrator) ImageToVideo(ctx context.Context, req worker.ImageToVi return orch.node.imageToVideo(ctx, req) } +func (orch *orchestrator) Upscale(ctx context.Context, req worker.UpscaleMultipartRequestBody) (*worker.ImageResponse, error) { + return orch.node.upscale(ctx, req) +} + func (orch *orchestrator) ProcessPayment(ctx context.Context, payment net.Payment, manifestID ManifestID) error { if orch.node == nil || orch.node.Recipient == nil { return nil @@ -937,6 +941,10 @@ func (n *LivepeerNode) imageToImage(ctx context.Context, req worker.ImageToImage return n.AIWorker.ImageToImage(ctx, req) } +func (n *LivepeerNode) upscale(ctx context.Context, req worker.UpscaleMultipartRequestBody) (*worker.ImageResponse, error) { + return n.AIWorker.Upscale(ctx, req) +} + func (n *LivepeerNode) imageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { // We might support generating more than one video in the future (i.e. multiple input images/prompts) numVideos := 1 diff --git a/go.mod b/go.mod index 5dd7d2c93..e77da1df2 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.7 + github.com/livepeer/ai-worker v0.0.8 github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 diff --git a/go.sum b/go.sum index b6430a64b..3073624a3 100644 --- a/go.sum +++ b/go.sum @@ -531,8 +531,8 @@ github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4n github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/livepeer/ai-worker v0.0.7 h1:mctm5jswdlMLUjaHeMhv3u4QtOAZ75jJhxgUdlzY5dU= -github.com/livepeer/ai-worker v0.0.7/go.mod h1:Xlnb0nFG2VsGeMG9hZmReVQXeFt0Dv28ODiUT2ooyLE= +github.com/livepeer/ai-worker v0.0.8 h1:FAjYJgSOaZslA06Wb6MolYohI30IMIujDTB26nfw8YE= +github.com/livepeer/ai-worker v0.0.8/go.mod h1:Xlnb0nFG2VsGeMG9hZmReVQXeFt0Dv28ODiUT2ooyLE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= diff --git a/server/ai_http.go b/server/ai_http.go index bc07b6350..fea249ca5 100644 --- a/server/ai_http.go +++ b/server/ai_http.go @@ -41,6 +41,7 @@ func startAIServer(lp lphttp) error { lp.transRPC.Handle("/text-to-image", oapiReqValidator(lp.TextToImage())) lp.transRPC.Handle("/image-to-image", oapiReqValidator(lp.ImageToImage())) lp.transRPC.Handle("/image-to-video", oapiReqValidator(lp.ImageToVideo())) + lp.transRPC.Handle("/upscale", oapiReqValidator(lp.Upscale())) return nil } @@ -108,6 +109,29 @@ func (h *lphttp) ImageToVideo() http.Handler { }) } +func (h *lphttp) Upscale() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + orch := h.orchestrator + + remoteAddr := getRemoteAddr(r) + ctx := clog.AddVal(r.Context(), clog.ClientIP, remoteAddr) + + multiRdr, err := r.MultipartReader() + if err != nil { + respondWithError(w, err.Error(), http.StatusBadRequest) + return + } + + var req worker.UpscaleMultipartRequestBody + if err := runtime.BindMultipart(&req, *multiRdr); err != nil { + respondWithError(w, err.Error(), http.StatusInternalServerError) + return + } + + handleAIRequest(ctx, w, r, orch, req) + }) +} + func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request, orch Orchestrator, req interface{}) { payment, err := getPayment(r.Header.Get(paymentHeader)) if err != nil { @@ -156,6 +180,25 @@ func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request return orch.ImageToImage(ctx, v) } + imageRdr, err := v.Image.Reader() + if err != nil { + respondWithError(w, err.Error(), http.StatusBadRequest) + return + } + config, _, err := image.DecodeConfig(imageRdr) + if err != nil { + respondWithError(w, err.Error(), http.StatusBadRequest) + return + } + outPixels = int64(config.Height) * int64(config.Width) + case worker.UpscaleMultipartRequestBody: + pipeline = "upscale" + cap = core.Capability_Upscale + modelID = *v.ModelId + submitFn = func(ctx context.Context) (*worker.ImageResponse, error) { + return orch.Upscale(ctx, v) + } + imageRdr, err := v.Image.Reader() if err != nil { respondWithError(w, err.Error(), http.StatusBadRequest) diff --git a/server/ai_mediaserver.go b/server/ai_mediaserver.go index ebaa54f17..ee58b340f 100644 --- a/server/ai_mediaserver.go +++ b/server/ai_mediaserver.go @@ -65,6 +65,7 @@ func startAIMediaServer(ls *LivepeerServer) error { ls.HTTPMux.Handle("/text-to-image", oapiReqValidator(ls.TextToImage())) ls.HTTPMux.Handle("/image-to-image", oapiReqValidator(ls.ImageToImage())) + ls.HTTPMux.Handle("/upscale", oapiReqValidator(ls.Upscale())) ls.HTTPMux.Handle("/image-to-video", oapiReqValidator(ls.ImageToVideo())) ls.HTTPMux.Handle("/image-to-video/result", ls.ImageToVideoResult()) @@ -271,6 +272,54 @@ func (ls *LivepeerServer) ImageToVideo() http.Handler { }) } +func (ls *LivepeerServer) Upscale() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + remoteAddr := getRemoteAddr(r) + ctx := clog.AddVal(r.Context(), clog.ClientIP, remoteAddr) + requestID := string(core.RandomManifestID()) + ctx = clog.AddVal(ctx, "request_id", requestID) + + multiRdr, err := r.MultipartReader() + if err != nil { + respondJsonError(ctx, w, err, http.StatusBadRequest) + return + } + + var req worker.UpscaleMultipartRequestBody + if err := runtime.BindMultipart(&req, *multiRdr); err != nil { + respondJsonError(ctx, w, err, http.StatusBadRequest) + return + } + + clog.V(common.VERBOSE).Infof(ctx, "Received Upscale request imageSize=%v prompt=%v model_id=%v", req.Image.FileSize(), req.Prompt, *req.ModelId) + + params := aiRequestParams{ + node: ls.LivepeerNode, + os: drivers.NodeStorage.NewSession(string(core.RandomManifestID())), + sessManager: ls.AISessionManager, + } + + start := time.Now() + resp, err := processUpscale(ctx, params, req) + if err != nil { + var e *ServiceUnavailableError + if errors.As(err, &e) { + respondJsonError(ctx, w, err, http.StatusServiceUnavailable) + return + } + respondJsonError(ctx, w, err, http.StatusInternalServerError) + return + } + + took := time.Since(start) + clog.V(common.VERBOSE).Infof(ctx, "Processed Upscale request imageSize=%v prompt=%v model_id=%v took=%v", req.Image.FileSize(), req.Prompt, *req.ModelId, took) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(resp) + }) +} + func (ls *LivepeerServer) ImageToVideoResult() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { remoteAddr := getRemoteAddr(r) diff --git a/server/ai_process.go b/server/ai_process.go index e851b8d82..697943304 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -27,6 +27,7 @@ const processingRetryTimeout = 2 * time.Second const defaultTextToImageModelID = "stabilityai/sdxl-turbo" const defaultImageToImageModelID = "stabilityai/sdxl-turbo" const defaultImageToVideoModelID = "stabilityai/stable-video-diffusion-img2vid-xt" +const defaultUpscaleModelID = "stabilityai/stable-diffusion-x4-upscaler" type ServiceUnavailableError struct { err error @@ -301,6 +302,86 @@ func submitImageToVideo(ctx context.Context, params aiRequestParams, sess *AISes return &res, nil } +func processUpscale(ctx context.Context, params aiRequestParams, req worker.UpscaleMultipartRequestBody) (*worker.ImageResponse, error) { + resp, err := processAIRequest(ctx, params, req) + if err != nil { + return nil, err + } + + newMedia := make([]worker.Media, len(resp.Images)) + for i, media := range resp.Images { + var data bytes.Buffer + writer := bufio.NewWriter(&data) + if err := worker.ReadImageB64DataUrl(media.Url, writer); err != nil { + return nil, err + } + writer.Flush() + + name := string(core.RandomManifestID()) + ".png" + newUrl, err := params.os.SaveData(ctx, name, bytes.NewReader(data.Bytes()), nil, 0) + if err != nil { + return nil, err + } + + newMedia[i] = worker.Media{Nsfw: media.Nsfw, Seed: media.Seed, Url: newUrl} + } + + resp.Images = newMedia + + return resp, nil +} + +func submitUpscale(ctx context.Context, params aiRequestParams, sess *AISession, req worker.UpscaleMultipartRequestBody) (*worker.ImageResponse, error) { + var buf bytes.Buffer + mw, err := worker.NewUpscaleMultipartWriter(&buf, req) + if err != nil { + return nil, err + } + + client, err := worker.NewClientWithResponses(sess.Transcoder(), worker.WithHTTPClient(httpClient)) + if err != nil { + return nil, err + } + + imageRdr, err := req.Image.Reader() + if err != nil { + return nil, err + } + config, _, err := image.DecodeConfig(imageRdr) + if err != nil { + return nil, err + } + outPixels := int64(config.Height) * int64(config.Width) + + setHeaders, balUpdate, err := prepareAIPayment(ctx, sess, outPixels) + if err != nil { + return nil, err + } + defer completeBalanceUpdate(sess.BroadcastSession, balUpdate) + + start := time.Now() + resp, err := client.UpscaleWithBodyWithResponse(ctx, mw.FormDataContentType(), &buf, setHeaders) + took := time.Since(start) + if err != nil { + return nil, err + } + + if resp.JSON200 == nil { + // TODO: Replace trim newline with better error spec from O + return nil, errors.New(strings.TrimSuffix(string(resp.Body), "\n")) + } + + // We treat a response as "receiving change" where the change is the difference between the credit and debit for the update + if balUpdate != nil { + balUpdate.Status = ReceivedChange + } + + // TODO: Refine this rough estimate in future iterations + sess.LatencyScore = took.Seconds() / float64(outPixels) + + return resp.JSON200, nil +} + func processAIRequest(ctx context.Context, params aiRequestParams, req interface{}) (*worker.ImageResponse, error) { var cap core.Capability var modelID string @@ -334,6 +415,15 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface submitFn = func(ctx context.Context, params aiRequestParams, sess *AISession) (*worker.ImageResponse, error) { return submitImageToVideo(ctx, params, sess, v) } + case worker.UpscaleMultipartRequestBody: + cap = core.Capability_Upscale + modelID = defaultUpscaleModelID + if v.ModelId != nil { + modelID = *v.ModelId + } + submitFn = func(ctx context.Context, params aiRequestParams, sess *AISession) (*worker.ImageResponse, error) { + return submitUpscale(ctx, params, sess, v) + } default: return nil, errors.New("unknown AI request type") } diff --git a/server/rpc.go b/server/rpc.go index ade60e49e..9c24d3336 100644 --- a/server/rpc.go +++ b/server/rpc.go @@ -66,6 +66,7 @@ type Orchestrator interface { TextToImage(ctx context.Context, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) ImageToImage(ctx context.Context, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) ImageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) + Upscale(ctx context.Context, req worker.UpscaleMultipartRequestBody) (*worker.ImageResponse, error) } // Balance describes methods for a session's balance maintenance From f2c9bb62b50d19698cd470263fce06854f77c7c1 Mon Sep 17 00:00:00 2001 From: ad-astra-video <99882368+ad-astra-video@users.noreply.github.com> Date: Thu, 4 Jul 2024 09:12:50 -0500 Subject: [PATCH 131/203] chore: update Image2Image and Upscale OS storage to use requestID similar to Text2Image and Image2Video (#3092) --- server/ai_mediaserver.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/ai_mediaserver.go b/server/ai_mediaserver.go index ee58b340f..62e5b7ad3 100644 --- a/server/ai_mediaserver.go +++ b/server/ai_mediaserver.go @@ -137,7 +137,7 @@ func (ls *LivepeerServer) ImageToImage() http.Handler { params := aiRequestParams{ node: ls.LivepeerNode, - os: drivers.NodeStorage.NewSession(string(core.RandomManifestID())), + os: drivers.NodeStorage.NewSession(requestID), sessManager: ls.AISessionManager, } @@ -295,7 +295,7 @@ func (ls *LivepeerServer) Upscale() http.Handler { params := aiRequestParams{ node: ls.LivepeerNode, - os: drivers.NodeStorage.NewSession(string(core.RandomManifestID())), + os: drivers.NodeStorage.NewSession(requestID), sessManager: ls.AISessionManager, } From 29d46033a638999c779483976b3e57f24b126616 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Mon, 8 Jul 2024 14:00:01 +0200 Subject: [PATCH 132/203] fix(ai): account for number of images in I2I latency score (#3093) This commit ensures that the I2I pipeline latency score calculation now considers the number of images. --- server/ai_process.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/server/ai_process.go b/server/ai_process.go index 697943304..ed58e3106 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -201,8 +201,14 @@ func submitImageToImage(ctx context.Context, params aiRequestParams, sess *AISes balUpdate.Status = ReceivedChange } - // TODO: Refine this rough estimate in future iterations - sess.LatencyScore = took.Seconds() / float64(outPixels) + // TODO: Refine this rough estimate in future iterations. + // TODO: Default values for the number of images is currently hardcoded. + // These should be managed by the nethttpmiddleware. Refer to issue LIV-412 for more details. + numImages := float64(1) + if req.NumImagesPerPrompt != nil { + numImages = float64(*req.NumImagesPerPrompt) + } + sess.LatencyScore = took.Seconds() / float64(outPixels) / numImages return resp.JSON200, nil } From 4825d680a34a9f5b28201e237437bc30411be4c2 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Wed, 17 Jul 2024 13:35:07 +0200 Subject: [PATCH 133/203] feat(ai): add 'num_inference_steps' to I2I,I2V and upscale pipeliens (#3099) This commit adds support for the `num_inference_steps` parameter to the I2I, I2V and upscale pipelines. It also fixes a incorrect latencyScore calculation for the bytedance model. --- core/ai.go | 17 +++++++++++++++++ server/ai_process.go | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/core/ai.go b/core/ai.go index c4a7146ec..8d14b21f1 100644 --- a/core/ai.go +++ b/core/ai.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "os" + "regexp" "strconv" "strings" @@ -85,3 +86,19 @@ func ParseAIModelConfigs(config string) ([]AIModelConfig, error) { return configs, nil } + +// parseStepsFromModelID parses the number of inference steps from the model ID suffix. +func ParseStepsFromModelID(modelID *string, defaultSteps float64) float64 { + numInferenceSteps := defaultSteps + + // Regular expression to find "_step" pattern anywhere in the model ID. + stepPattern := regexp.MustCompile(`_(\d+)step`) + matches := stepPattern.FindStringSubmatch(*modelID) + if len(matches) == 2 { + if parsedSteps, err := strconv.Atoi(matches[1]); err == nil { + numInferenceSteps = float64(parsedSteps) + } + } + + return numInferenceSteps +} diff --git a/server/ai_process.go b/server/ai_process.go index ed58e3106..e542b1fb1 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -122,6 +122,11 @@ func submitTextToImage(ctx context.Context, params aiRequestParams, sess *AISess if req.NumInferenceSteps != nil { numInferenceSteps = float64(*req.NumInferenceSteps) } + // Handle special case for SDXL-Lightning model. + if strings.HasPrefix(*req.ModelId, "ByteDance/SDXL-Lightning") { + numInferenceSteps = core.ParseStepsFromModelID(req.ModelId, 8) + } + sess.LatencyScore = took.Seconds() / float64(outPixels) / (numImages * numInferenceSteps) return resp.JSON200, nil @@ -202,13 +207,22 @@ func submitImageToImage(ctx context.Context, params aiRequestParams, sess *AISes } // TODO: Refine this rough estimate in future iterations. - // TODO: Default values for the number of images is currently hardcoded. + // TODO: Default values for the number of images and inference steps are currently hardcoded. // These should be managed by the nethttpmiddleware. Refer to issue LIV-412 for more details. numImages := float64(1) if req.NumImagesPerPrompt != nil { numImages = float64(*req.NumImagesPerPrompt) } - sess.LatencyScore = took.Seconds() / float64(outPixels) / numImages + numInferenceSteps := float64(100) + if req.NumInferenceSteps != nil { + numInferenceSteps = float64(*req.NumInferenceSteps) + } + // Handle special case for SDXL-Lightning model. + if strings.HasPrefix(*req.ModelId, "ByteDance/SDXL-Lightning") { + numInferenceSteps = core.ParseStepsFromModelID(req.ModelId, 8) + } + + sess.LatencyScore = took.Seconds() / float64(outPixels) / (numImages * numInferenceSteps) return resp.JSON200, nil } @@ -303,7 +317,13 @@ func submitImageToVideo(ctx context.Context, params aiRequestParams, sess *AISes } // TODO: Refine this rough estimate in future iterations - sess.LatencyScore = took.Seconds() / float64(outPixels) + // TODO: Default values for the number of inference steps is currently hardcoded. + // These should be managed by the nethttpmiddleware. Refer to issue LIV-412 for more details. + numInferenceSteps := float64(25) + if req.NumInferenceSteps != nil { + numInferenceSteps = float64(*req.NumInferenceSteps) + } + sess.LatencyScore = took.Seconds() / float64(outPixels) / numInferenceSteps return &res, nil } @@ -383,7 +403,13 @@ func submitUpscale(ctx context.Context, params aiRequestParams, sess *AISession, } // TODO: Refine this rough estimate in future iterations - sess.LatencyScore = took.Seconds() / float64(outPixels) + // TODO: Default values for the number of inference steps is currently hardcoded. + // These should be managed by the nethttpmiddleware. Refer to issue LIV-412 for more details. + numInferenceSteps := float64(75) + if req.NumInferenceSteps != nil { + numInferenceSteps = float64(*req.NumInferenceSteps) + } + sess.LatencyScore = took.Seconds() / float64(outPixels) / numInferenceSteps return resp.JSON200, nil } From 2ab10c633606ed2601ee164978d43a803aabc055 Mon Sep 17 00:00:00 2001 From: John | Elite Encoder Date: Wed, 17 Jul 2024 09:50:55 -0400 Subject: [PATCH 134/203] feat: add audio-to-text pipeline (#3078) * Add speech-to-text pipeline, refactor processAIRequest and handleAIRequest to allow for various response types * Pin gomod to ai-runner for testing * Revert "Pin gomod to ai-runner for testing" This reverts commit d4ba500dda02431f3627d7979d01e299c071bdec. * Update go mod dep for ai-worker * Calculate pixel value of audio file * fix go-mod deps * Adjust price calculation * one second per pixel * cleanup, fix missing duration * Add supported file types, calculate price by milliseconds * Add bad request response for unsupported file types * Update name of function * Update go mod to ai-runner * Use ffmpeg to get duration * update install_ffmpeg.sh to parse audio better * Check for audio codec instead of video codec * gomod edits * add docker file * Update install_ffmpeg.sh to improve audio support, Add duration validation and logging, pin lpms * rename speech-to-text to audio-to-text * Update go-mod * cleanup * update go mod * remove comment * update gomod * Update lpms mod * Update to latest lpms * Update lpms * feat(ai): apply code improvements to AudioToText pipeline This commit applies several code improvements to the AudioToText codebase. * Remove unnecessary logic * Remove unused error * Fix missing err * Update go.mod and tidy * chore(ai): update ai-worker and lpms to latest version This commit ensures that the ai-worker and lpms are at the latest versions which contain the changes needed for the audio-to-text pipeline. --------- Co-authored-by: 0xb79orch <0xb79orch@gmail.com> Co-authored-by: Rick Staa --- cmd/livepeer/starter/starter.go | 12 +++ common/util.go | 25 ++++++ core/ai.go | 1 + core/capabilities.go | 3 + core/orchestrator.go | 8 ++ go.mod | 9 +- go.sum | 20 ++--- install_ffmpeg.sh | 8 +- server/ai_http.go | 48 ++++++++-- server/ai_mediaserver.go | 54 ++++++++++++ server/ai_process.go | 149 ++++++++++++++++++++++++++------ server/rpc.go | 1 + 12 files changed, 289 insertions(+), 49 deletions(-) diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index 15cca7f8c..1495101ce 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -613,6 +613,18 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { constraints[core.Capability_Upscale].Models[config.ModelID] = modelConstraint n.SetBasePriceForCap("default", core.Capability_Upscale, config.ModelID, big.NewRat(config.PricePerUnit, config.PixelsPerUnit)) + case "audio-to-text": + _, ok := constraints[core.Capability_AudioToText] + if !ok { + aiCaps = append(aiCaps, core.Capability_AudioToText) + constraints[core.Capability_AudioToText] = &core.Constraints{ + Models: make(map[string]*core.ModelConstraint), + } + } + + constraints[core.Capability_AudioToText].Models[config.ModelID] = modelConstraint + + n.SetBasePriceForCap("default", core.Capability_AudioToText, config.ModelID, big.NewRat(config.PricePerUnit, config.PixelsPerUnit)) } if len(aiCaps) > 0 { diff --git a/common/util.go b/common/util.go index 639cf07cc..8a9eb1521 100644 --- a/common/util.go +++ b/common/util.go @@ -25,6 +25,7 @@ import ( "github.com/jaypipes/ghw/pkg/pci" "github.com/livepeer/go-livepeer/net" ffmpeg "github.com/livepeer/lpms/ffmpeg" + "github.com/oapi-codegen/runtime/types" "github.com/pkg/errors" "google.golang.org/grpc/peer" ) @@ -74,6 +75,8 @@ var ( ErrProfEncoder = fmt.Errorf("unknown VideoProfile encoder for protobufs") ErrProfName = fmt.Errorf("unknown VideoProfile profile name") + ErrAudioDurationCalculation = fmt.Errorf("audio duration calculation failed") + ext2mime = map[string]string{ ".ts": "video/mp2t", ".mp4": "video/mp4", @@ -530,3 +533,25 @@ func ParseEthAddr(strJsonKey string) (string, error) { } return "", errors.New("Error parsing address from keyfile") } + +// CalculateAudioDuration calculates audio file duration using the lpms/ffmpeg package. +func CalculateAudioDuration(audio types.File) (int64, error) { + read, err := audio.Reader() + if err != nil { + return 0, err + } + defer read.Close() + + bytearr, _ := audio.Bytes() + _, mediaFormat, err := ffmpeg.GetCodecInfoBytes(bytearr) + if err != nil { + return 0, errors.New("Error getting codec info") + } + + duration := int64(mediaFormat.DurSecs) + if duration <= 0 { + return 0, ErrAudioDurationCalculation + } + + return duration, nil +} diff --git a/core/ai.go b/core/ai.go index 8d14b21f1..772712e97 100644 --- a/core/ai.go +++ b/core/ai.go @@ -17,6 +17,7 @@ type AI interface { ImageToImage(context.Context, worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) ImageToVideo(context.Context, worker.ImageToVideoMultipartRequestBody) (*worker.VideoResponse, error) Upscale(context.Context, worker.UpscaleMultipartRequestBody) (*worker.ImageResponse, error) + AudioToText(context.Context, worker.AudioToTextMultipartRequestBody) (*worker.TextResponse, error) Warm(context.Context, string, string, worker.RunnerEndpoint, worker.OptimizationFlags) error Stop(context.Context) error HasCapacity(pipeline, modelID string) bool diff --git a/core/capabilities.go b/core/capabilities.go index 240222ab2..12eb7bbc6 100644 --- a/core/capabilities.go +++ b/core/capabilities.go @@ -71,6 +71,7 @@ const ( Capability_ImageToImage Capability_ImageToVideo Capability_Upscale + Capability_AudioToText ) var CapabilityNameLookup = map[Capability]string{ @@ -106,6 +107,7 @@ var CapabilityNameLookup = map[Capability]string{ Capability_ImageToImage: "Image to image", Capability_ImageToVideo: "Image to video", Capability_Upscale: "Upscale", + Capability_AudioToText: "Audio to text", } var CapabilityTestLookup = map[Capability]CapabilityTest{ @@ -195,6 +197,7 @@ func OptionalCapabilities() []Capability { Capability_ImageToImage, Capability_ImageToVideo, Capability_Upscale, + Capability_AudioToText, } } diff --git a/core/orchestrator.go b/core/orchestrator.go index f9d42b3fa..4ceea94bb 100644 --- a/core/orchestrator.go +++ b/core/orchestrator.go @@ -126,6 +126,10 @@ func (orch *orchestrator) Upscale(ctx context.Context, req worker.UpscaleMultipa return orch.node.upscale(ctx, req) } +func (orch *orchestrator) AudioToText(ctx context.Context, req worker.AudioToTextMultipartRequestBody) (*worker.TextResponse, error) { + return orch.node.AudioToText(ctx, req) +} + func (orch *orchestrator) ProcessPayment(ctx context.Context, payment net.Payment, manifestID ManifestID) error { if orch.node == nil || orch.node.Recipient == nil { return nil @@ -945,6 +949,10 @@ func (n *LivepeerNode) upscale(ctx context.Context, req worker.UpscaleMultipartR return n.AIWorker.Upscale(ctx, req) } +func (n *LivepeerNode) AudioToText(ctx context.Context, req worker.AudioToTextMultipartRequestBody) (*worker.TextResponse, error) { + return n.AIWorker.AudioToText(ctx, req) +} + func (n *LivepeerNode) imageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { // We might support generating more than one video in the future (i.e. multiple input images/prompts) numVideos := 1 diff --git a/go.mod b/go.mod index e77da1df2..c80fe64f3 100644 --- a/go.mod +++ b/go.mod @@ -9,13 +9,13 @@ require ( github.com/getkin/kin-openapi v0.124.0 github.com/golang/glog v1.1.1 github.com/golang/mock v1.6.0 - github.com/golang/protobuf v1.5.3 + github.com/golang/protobuf v1.5.4 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.0.8 + github.com/livepeer/ai-worker v0.1.0 github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 - github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 + github.com/livepeer/lpms v0.0.0-20240711175220-227325841434 github.com/livepeer/m3u8 v0.11.1 github.com/mattn/go-sqlite3 v1.14.18 github.com/oapi-codegen/nethttp-middleware v1.0.1 @@ -32,7 +32,7 @@ require ( go.uber.org/goleak v1.3.0 golang.org/x/net v0.25.0 google.golang.org/grpc v1.57.1 - google.golang.org/protobuf v1.31.0 + google.golang.org/protobuf v1.33.0 pgregory.net/rapid v1.1.0 ) @@ -85,6 +85,7 @@ require ( github.com/go-openapi/swag v0.22.8 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/go-stack/stack v1.8.1 // indirect + github.com/go-test/deep v1.1.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect diff --git a/go.sum b/go.sum index 3073624a3..74df1b726 100644 --- a/go.sum +++ b/go.sum @@ -251,8 +251,8 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= -github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= -github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= +github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -297,8 +297,8 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -531,16 +531,16 @@ github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4n github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/livepeer/ai-worker v0.0.8 h1:FAjYJgSOaZslA06Wb6MolYohI30IMIujDTB26nfw8YE= -github.com/livepeer/ai-worker v0.0.8/go.mod h1:Xlnb0nFG2VsGeMG9hZmReVQXeFt0Dv28ODiUT2ooyLE= +github.com/livepeer/ai-worker v0.1.0 h1:SJBZuxeK0vEzJPBzf5osdgVCxHYZt7ZKR2CvZ7Q7iog= +github.com/livepeer/ai-worker v0.1.0/go.mod h1:Xlnb0nFG2VsGeMG9hZmReVQXeFt0Dv28ODiUT2ooyLE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded/go.mod h1:xkDdm+akniYxVT9KW1Y2Y7Hso6aW+rZObz3nrA9yTHw= github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 h1:4oH3NqV0NvcdS44Ld3zK2tO8IUiNozIggm74yobQeZg= github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18/go.mod h1:Jpf4jHK+fbWioBHRDRM1WadNT1qmY27g2YicTdO0Rtc= -github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69 h1:4A6geMb+HfxBBfaS24t8R3ddpEDfWbpx7NTQZMt5Fp4= -github.com/livepeer/lpms v0.0.0-20240120150405-de94555cdc69/go.mod h1:Hr/JhxxPDipOVd4ZrGYWrdJfpVF8/SEI0nNr2ctAlkM= +github.com/livepeer/lpms v0.0.0-20240711175220-227325841434 h1:E7PKN6q/jMLapEV+eEwlwv87Xe5zacaVhvZ8T6AJR3c= +github.com/livepeer/lpms v0.0.0-20240711175220-227325841434/go.mod h1:Hr/JhxxPDipOVd4ZrGYWrdJfpVF8/SEI0nNr2ctAlkM= github.com/livepeer/m3u8 v0.11.1 h1:VkUJzfNTyjy9mqsgp5JPvouwna8wGZMvd/gAfT5FinU= github.com/livepeer/m3u8 v0.11.1/go.mod h1:IUqAtwWPAG2CblfQa4SVzTQoDcEMPyfNOaBSxqHMS04= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= @@ -1217,8 +1217,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/install_ffmpeg.sh b/install_ffmpeg.sh index bb99292ef..df864b496 100755 --- a/install_ffmpeg.sh +++ b/install_ffmpeg.sh @@ -208,13 +208,13 @@ if [[ ! -e "$ROOT/ffmpeg/libavcodec/libavcodec.a" ]]; then ./configure ${TARGET_OS:-} $DISABLE_FFMPEG_COMPONENTS --fatal-warnings \ --enable-libx264 --enable-gpl \ --enable-protocol=rtmp,file,pipe \ - --enable-muxer=mpegts,hls,segment,mp4,hevc,matroska,webm,null --enable-demuxer=flv,mpegts,mp4,mov,webm,matroska,image2 \ + --enable-muxer=mp3,wav,flac,mpegts,hls,segment,mp4,hevc,matroska,webm,null --enable-demuxer=mp3,wav,flac,flv,mpegts,mp4,mov,webm,matroska,image2 \ --enable-bsf=h264_mp4toannexb,aac_adtstoasc,h264_metadata,h264_redundant_pps,hevc_mp4toannexb,extract_extradata \ - --enable-parser=aac,aac_latm,h264,hevc,vp8,vp9,png \ + --enable-parser=mpegaudio,vorbis,opus,flac,aac,aac_latm,h264,hevc,vp8,vp9,png \ --enable-filter=abuffer,buffer,abuffersink,buffersink,afifo,fifo,aformat,format \ --enable-filter=aresample,asetnsamples,fps,scale,hwdownload,select,livepeer_dnn,signature \ - --enable-encoder=aac,opus,libx264 \ - --enable-decoder=aac,opus,h264,png \ + --enable-encoder=mp3,vorbis,flac,aac,opus,libx264 \ + --enable-decoder=mp3,vorbis,flac,aac,opus,h264,png \ --extra-cflags="${EXTRA_CFLAGS} -I${ROOT}/compiled/include -I/usr/local/cuda/include" \ --extra-ldflags="${EXTRA_FFMPEG_LDFLAGS} -L${ROOT}/compiled/lib -L/usr/local/cuda/lib64" \ --prefix="$ROOT/compiled" \ diff --git a/server/ai_http.go b/server/ai_http.go index fea249ca5..4ef6eac54 100644 --- a/server/ai_http.go +++ b/server/ai_http.go @@ -42,6 +42,7 @@ func startAIServer(lp lphttp) error { lp.transRPC.Handle("/image-to-image", oapiReqValidator(lp.ImageToImage())) lp.transRPC.Handle("/image-to-video", oapiReqValidator(lp.ImageToVideo())) lp.transRPC.Handle("/upscale", oapiReqValidator(lp.Upscale())) + lp.transRPC.Handle("/audio-to-text", oapiReqValidator(lp.AudioToText())) return nil } @@ -132,6 +133,29 @@ func (h *lphttp) Upscale() http.Handler { }) } +func (h *lphttp) AudioToText() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + orch := h.orchestrator + + remoteAddr := getRemoteAddr(r) + ctx := clog.AddVal(r.Context(), clog.ClientIP, remoteAddr) + + multiRdr, err := r.MultipartReader() + if err != nil { + respondWithError(w, err.Error(), http.StatusBadRequest) + return + } + + var req worker.AudioToTextMultipartRequestBody + if err := runtime.BindMultipart(&req, *multiRdr); err != nil { + respondWithError(w, err.Error(), http.StatusInternalServerError) + return + } + + handleAIRequest(ctx, w, r, orch, req) + }) +} + func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request, orch Orchestrator, req interface{}) { payment, err := getPayment(r.Header.Get(paymentHeader)) if err != nil { @@ -149,7 +173,7 @@ func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request var cap core.Capability var pipeline string var modelID string - var submitFn func(context.Context) (*worker.ImageResponse, error) + var submitFn func(context.Context) (interface{}, error) var outPixels int64 switch v := req.(type) { @@ -157,7 +181,7 @@ func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request pipeline = "text-to-image" cap = core.Capability_TextToImage modelID = *v.ModelId - submitFn = func(ctx context.Context) (*worker.ImageResponse, error) { + submitFn = func(ctx context.Context) (interface{}, error) { return orch.TextToImage(ctx, v) } @@ -176,7 +200,7 @@ func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request pipeline = "image-to-image" cap = core.Capability_ImageToImage modelID = *v.ModelId - submitFn = func(ctx context.Context) (*worker.ImageResponse, error) { + submitFn = func(ctx context.Context) (interface{}, error) { return orch.ImageToImage(ctx, v) } @@ -195,7 +219,7 @@ func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request pipeline = "upscale" cap = core.Capability_Upscale modelID = *v.ModelId - submitFn = func(ctx context.Context) (*worker.ImageResponse, error) { + submitFn = func(ctx context.Context) (interface{}, error) { return orch.Upscale(ctx, v) } @@ -214,7 +238,7 @@ func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request pipeline = "image-to-video" cap = core.Capability_ImageToVideo modelID = *v.ModelId - submitFn = func(ctx context.Context) (*worker.ImageResponse, error) { + submitFn = func(ctx context.Context) (interface{}, error) { return orch.ImageToVideo(ctx, v) } @@ -231,6 +255,20 @@ func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request frames := int64(25) outPixels = height * width * int64(frames) + case worker.AudioToTextMultipartRequestBody: + pipeline = "audio-to-text" + cap = core.Capability_AudioToText + modelID = *v.ModelId + submitFn = func(ctx context.Context) (interface{}, error) { + return orch.AudioToText(ctx, v) + } + + outPixels, err = common.CalculateAudioDuration(v.Audio) + if err != nil { + respondWithError(w, "Unable to calculate duration", http.StatusBadRequest) + return + } + outPixels *= 1000 // Convert to milliseconds default: respondWithError(w, "Unknown request type", http.StatusBadRequest) return diff --git a/server/ai_mediaserver.go b/server/ai_mediaserver.go index 62e5b7ad3..29d33b9fe 100644 --- a/server/ai_mediaserver.go +++ b/server/ai_mediaserver.go @@ -68,6 +68,7 @@ func startAIMediaServer(ls *LivepeerServer) error { ls.HTTPMux.Handle("/upscale", oapiReqValidator(ls.Upscale())) ls.HTTPMux.Handle("/image-to-video", oapiReqValidator(ls.ImageToVideo())) ls.HTTPMux.Handle("/image-to-video/result", ls.ImageToVideoResult()) + ls.HTTPMux.Handle("/audio-to-text", oapiReqValidator(ls.AudioToText())) return nil } @@ -320,6 +321,59 @@ func (ls *LivepeerServer) Upscale() http.Handler { }) } +func (ls *LivepeerServer) AudioToText() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + remoteAddr := getRemoteAddr(r) + ctx := clog.AddVal(r.Context(), clog.ClientIP, remoteAddr) + requestID := string(core.RandomManifestID()) + ctx = clog.AddVal(ctx, "request_id", requestID) + + multiRdr, err := r.MultipartReader() + if err != nil { + respondJsonError(ctx, w, err, http.StatusBadRequest) + return + } + + var req worker.AudioToTextMultipartRequestBody + if err := runtime.BindMultipart(&req, *multiRdr); err != nil { + respondJsonError(ctx, w, err, http.StatusBadRequest) + return + } + + clog.V(common.VERBOSE).Infof(ctx, "Received AudioToText request audioSize=%v model_id=%v", req.Audio.FileSize(), *req.ModelId) + + params := aiRequestParams{ + node: ls.LivepeerNode, + os: drivers.NodeStorage.NewSession(requestID), + sessManager: ls.AISessionManager, + } + + start := time.Now() + resp, err := processAudioToText(ctx, params, req) + if err != nil { + var serviceUnavailableErr *ServiceUnavailableError + var badRequestErr *BadRequestError + if errors.As(err, &serviceUnavailableErr) { + respondJsonError(ctx, w, err, http.StatusServiceUnavailable) + return + } + if errors.As(err, &badRequestErr) { + respondJsonError(ctx, w, err, http.StatusBadRequest) + return + } + respondJsonError(ctx, w, err, http.StatusInternalServerError) + return + } + + took := time.Since(start) + clog.V(common.VERBOSE).Infof(ctx, "Processed AudioToText request model_id=%v took=%v", *req.ModelId, took) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(resp) + }) +} + func (ls *LivepeerServer) ImageToVideoResult() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { remoteAddr := getRemoteAddr(r) diff --git a/server/ai_process.go b/server/ai_process.go index e542b1fb1..0f5ab335f 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -28,6 +28,7 @@ const defaultTextToImageModelID = "stabilityai/sdxl-turbo" const defaultImageToImageModelID = "stabilityai/sdxl-turbo" const defaultImageToVideoModelID = "stabilityai/stable-video-diffusion-img2vid-xt" const defaultUpscaleModelID = "stabilityai/stable-diffusion-x4-upscaler" +const defaultAudioToTextModelID = "openai/whisper-large-v3" type ServiceUnavailableError struct { err error @@ -37,6 +38,14 @@ func (e *ServiceUnavailableError) Error() string { return e.err.Error() } +type BadRequestError struct { + err error +} + +func (e *BadRequestError) Error() string { + return e.err.Error() +} + type aiRequestParams struct { node *core.LivepeerNode os drivers.OSSession @@ -49,8 +58,10 @@ func processTextToImage(ctx context.Context, params aiRequestParams, req worker. return nil, err } - newMedia := make([]worker.Media, len(resp.Images)) - for i, media := range resp.Images { + imgResp := resp.(*worker.ImageResponse) + + newMedia := make([]worker.Media, len(imgResp.Images)) + for i, media := range imgResp.Images { var data bytes.Buffer writer := bufio.NewWriter(&data) if err := worker.ReadImageB64DataUrl(media.Url, writer); err != nil { @@ -67,9 +78,9 @@ func processTextToImage(ctx context.Context, params aiRequestParams, req worker. newMedia[i] = worker.Media{Nsfw: media.Nsfw, Seed: media.Seed, Url: newUrl} } - resp.Images = newMedia + imgResp.Images = newMedia - return resp, nil + return imgResp, nil } func submitTextToImage(ctx context.Context, params aiRequestParams, sess *AISession, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) { @@ -138,8 +149,10 @@ func processImageToImage(ctx context.Context, params aiRequestParams, req worker return nil, err } - newMedia := make([]worker.Media, len(resp.Images)) - for i, media := range resp.Images { + imgResp := resp.(*worker.ImageResponse) + + newMedia := make([]worker.Media, len(imgResp.Images)) + for i, media := range imgResp.Images { var data bytes.Buffer writer := bufio.NewWriter(&data) if err := worker.ReadImageB64DataUrl(media.Url, writer); err != nil { @@ -156,9 +169,9 @@ func processImageToImage(ctx context.Context, params aiRequestParams, req worker newMedia[i] = worker.Media{Nsfw: media.Nsfw, Seed: media.Seed, Url: newUrl} } - resp.Images = newMedia + imgResp.Images = newMedia - return resp, nil + return imgResp, nil } func submitImageToImage(ctx context.Context, params aiRequestParams, sess *AISession, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { @@ -234,8 +247,11 @@ func processImageToVideo(ctx context.Context, params aiRequestParams, req worker } // HACK: Re-use worker.ImageResponse to return results - videos := make([]worker.Media, len(resp.Images)) - for i, media := range resp.Images { + // TODO: Refactor to return worker.VideoResponse + imgResp := resp.(*worker.ImageResponse) + + videos := make([]worker.Media, len(imgResp.Images)) + for i, media := range imgResp.Images { data, err := downloadSeg(ctx, media.Url) if err != nil { return nil, err @@ -255,9 +271,9 @@ func processImageToVideo(ctx context.Context, params aiRequestParams, req worker } - resp.Images = videos + imgResp.Images = videos - return resp, nil + return imgResp, nil } func submitImageToVideo(ctx context.Context, params aiRequestParams, sess *AISession, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { @@ -334,8 +350,10 @@ func processUpscale(ctx context.Context, params aiRequestParams, req worker.Upsc return nil, err } - newMedia := make([]worker.Media, len(resp.Images)) - for i, media := range resp.Images { + imgResp := resp.(*worker.ImageResponse) + + newMedia := make([]worker.Media, len(imgResp.Images)) + for i, media := range imgResp.Images { var data bytes.Buffer writer := bufio.NewWriter(&data) if err := worker.ReadImageB64DataUrl(media.Url, writer); err != nil { @@ -352,9 +370,9 @@ func processUpscale(ctx context.Context, params aiRequestParams, req worker.Upsc newMedia[i] = worker.Media{Nsfw: media.Nsfw, Seed: media.Seed, Url: newUrl} } - resp.Images = newMedia + imgResp.Images = newMedia - return resp, nil + return imgResp, nil } func submitUpscale(ctx context.Context, params aiRequestParams, sess *AISession, req worker.UpscaleMultipartRequestBody) (*worker.ImageResponse, error) { @@ -414,10 +432,78 @@ func submitUpscale(ctx context.Context, params aiRequestParams, sess *AISession, return resp.JSON200, nil } -func processAIRequest(ctx context.Context, params aiRequestParams, req interface{}) (*worker.ImageResponse, error) { +func submitAudioToText(ctx context.Context, params aiRequestParams, sess *AISession, req worker.AudioToTextMultipartRequestBody) (*worker.TextResponse, error) { + var buf bytes.Buffer + mw, err := worker.NewAudioToTextMultipartWriter(&buf, req) + if err != nil { + return nil, err + } + + client, err := worker.NewClientWithResponses(sess.Transcoder(), worker.WithHTTPClient(httpClient)) + if err != nil { + return nil, err + } + + durationSeconds, err := common.CalculateAudioDuration(req.Audio) + if err != nil { + return nil, err + } + + clog.V(common.VERBOSE).Infof(ctx, "Submitting audio-to-text media with duration: %d seconds", durationSeconds) + setHeaders, balUpdate, err := prepareAIPayment(ctx, sess, durationSeconds*1000) + if err != nil { + return nil, err + } + defer completeBalanceUpdate(sess.BroadcastSession, balUpdate) + + start := time.Now() + resp, err := client.AudioToTextWithBody(ctx, mw.FormDataContentType(), &buf, setHeaders) + took := time.Since(start) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + data, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + if resp.StatusCode != 200 { + return nil, errors.New(string(data)) + } + + // We treat a response as "receiving change" where the change is the difference between the credit and debit for the update + if balUpdate != nil { + balUpdate.Status = ReceivedChange + } + + var res worker.TextResponse + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + + // TODO: Refine this rough estimate in future iterations + sess.LatencyScore = took.Seconds() / float64(durationSeconds) + + return &res, nil +} + +func processAudioToText(ctx context.Context, params aiRequestParams, req worker.AudioToTextMultipartRequestBody) (*worker.TextResponse, error) { + resp, err := processAIRequest(ctx, params, req) + if err != nil { + return nil, err + } + + txtResp := resp.(*worker.TextResponse) + + return txtResp, nil +} + +func processAIRequest(ctx context.Context, params aiRequestParams, req interface{}) (interface{}, error) { var cap core.Capability var modelID string - var submitFn func(context.Context, aiRequestParams, *AISession) (*worker.ImageResponse, error) + var submitFn func(context.Context, aiRequestParams, *AISession) (interface{}, error) switch v := req.(type) { case worker.TextToImageJSONRequestBody: @@ -426,7 +512,7 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface if v.ModelId != nil { modelID = *v.ModelId } - submitFn = func(ctx context.Context, params aiRequestParams, sess *AISession) (*worker.ImageResponse, error) { + submitFn = func(ctx context.Context, params aiRequestParams, sess *AISession) (interface{}, error) { return submitTextToImage(ctx, params, sess, v) } case worker.ImageToImageMultipartRequestBody: @@ -435,7 +521,7 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface if v.ModelId != nil { modelID = *v.ModelId } - submitFn = func(ctx context.Context, params aiRequestParams, sess *AISession) (*worker.ImageResponse, error) { + submitFn = func(ctx context.Context, params aiRequestParams, sess *AISession) (interface{}, error) { return submitImageToImage(ctx, params, sess, v) } case worker.ImageToVideoMultipartRequestBody: @@ -444,7 +530,7 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface if v.ModelId != nil { modelID = *v.ModelId } - submitFn = func(ctx context.Context, params aiRequestParams, sess *AISession) (*worker.ImageResponse, error) { + submitFn = func(ctx context.Context, params aiRequestParams, sess *AISession) (interface{}, error) { return submitImageToVideo(ctx, params, sess, v) } case worker.UpscaleMultipartRequestBody: @@ -453,14 +539,23 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface if v.ModelId != nil { modelID = *v.ModelId } - submitFn = func(ctx context.Context, params aiRequestParams, sess *AISession) (*worker.ImageResponse, error) { + submitFn = func(ctx context.Context, params aiRequestParams, sess *AISession) (interface{}, error) { return submitUpscale(ctx, params, sess, v) } + case worker.AudioToTextMultipartRequestBody: + cap = core.Capability_AudioToText + modelID = defaultAudioToTextModelID + if v.ModelId != nil { + modelID = *v.ModelId + } + submitFn = func(ctx context.Context, params aiRequestParams, sess *AISession) (interface{}, error) { + return submitAudioToText(ctx, params, sess, v) + } default: - return nil, errors.New("unknown AI request type") + return nil, fmt.Errorf("unsupported request type %T", req) } - var resp *worker.ImageResponse + var resp interface{} cctx, cancel := context.WithTimeout(ctx, processingRetryTimeout) defer cancel() @@ -491,14 +586,16 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface } clog.Infof(ctx, "Error submitting request cap=%v modelID=%v try=%v orch=%v err=%v", cap, modelID, tries, sess.Transcoder(), err) - params.sessManager.Remove(ctx, sess) + + if errors.Is(err, common.ErrAudioDurationCalculation) { + return nil, &BadRequestError{err} + } } if resp == nil { return nil, &ServiceUnavailableError{err: errors.New("no orchestrators available")} } - return resp, nil } diff --git a/server/rpc.go b/server/rpc.go index 9c24d3336..0fc46494f 100644 --- a/server/rpc.go +++ b/server/rpc.go @@ -67,6 +67,7 @@ type Orchestrator interface { ImageToImage(ctx context.Context, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) ImageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) Upscale(ctx context.Context, req worker.UpscaleMultipartRequestBody) (*worker.ImageResponse, error) + AudioToText(ctx context.Context, req worker.AudioToTextMultipartRequestBody) (*worker.TextResponse, error) } // Balance describes methods for a session's balance maintenance From f60c0c5a267b75aab0276166bc6e1896e2642ac8 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Wed, 17 Jul 2024 17:51:09 +0200 Subject: [PATCH 135/203] feat(ai): add AI gateway metrics (#3087) * Add gateway metric for roundtrip ai times by model and pipeline * Rename metrics and add unique manifest * Fix name mismatch * modelsRequested not working correctly * feat: add initial POC AI gateway metrics This commit adds the initial AI gateway metrics so that they can reviewed by others. The code still need to be cleaned up and the buckets adjusted. * feat: improve AI metrics This commit improves the AI metrics so that they are easier to work with. * feat(ai): log no capacity error to metrics This commit ensures that an error is logged when the Gateway could not find orchestrators for a given model and capability. * feat(ai): add TicketValueSent and TicketsSent metrics This commit ensure that the `ticket_value_sent` abd `tickets_sent` metrics are also created for a AI Gateway. * fix(ai): ensure that AI metrics have orch address label This commit ensures that the AI gateway metrics contain the orch address label. * fix(ai): fix incorrect Gateway pricing metric This commit ensures that the AI job pricing is calculated correctly and cleans up the codebase. * refactor(ai): remove Orch label from ai_request_price metric This commit removes the Orch label from the ai_request_price metrics since that information is better to be retrieved from another endpoint. --------- Co-authored-by: Elite Encoder --- monitor/census.go | 107 ++++++++++++++++++++++++++++++++++++++ server/ai_process.go | 121 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 226 insertions(+), 2 deletions(-) diff --git a/monitor/census.go b/monitor/census.go index f89d1a41a..373220122 100644 --- a/monitor/census.go +++ b/monitor/census.go @@ -113,6 +113,8 @@ type ( kOrchestratorURI tag.Key kOrchestratorAddress tag.Key kFVErrorType tag.Key + kPipeline tag.Key + kModelName tag.Key mSegmentSourceAppeared *stats.Int64Measure mSegmentEmerged *stats.Int64Measure mSegmentEmergedUnprocessed *stats.Int64Measure @@ -190,6 +192,12 @@ type ( mSegmentClassProb *stats.Float64Measure mSceneClassification *stats.Int64Measure + // Metrics for AI jobs + mAIModelsRequested *stats.Int64Measure + mAIRequestLatencyScore *stats.Float64Measure + mAIRequestPrice *stats.Float64Measure + mAIRequestError *stats.Int64Measure + lock sync.Mutex emergeTimes map[uint64]map[uint64]time.Time // nonce:seqNo success map[uint64]*segmentsAverager @@ -217,6 +225,11 @@ type ( removedAt time.Time tries map[uint64]tryData // seqNo:try } + + AIJobInfo struct { + LatencyScore float64 + PricePerUnit float64 + } ) // Exporter Prometheus exporter that handles `/metrics` endpoint @@ -254,6 +267,8 @@ func InitCensus(nodeType NodeType, version string) { census.kOrchestratorAddress = tag.MustNewKey("orchestrator_address") census.kFVErrorType = tag.MustNewKey("fverror_type") census.kSegClassName = tag.MustNewKey("seg_class_name") + census.kModelName = tag.MustNewKey("model_name") + census.kPipeline = tag.MustNewKey("pipeline") census.ctx, err = tag.New(ctx, tag.Insert(census.kNodeType, string(nodeType)), tag.Insert(census.kNodeID, NodeID)) if err != nil { glog.Exit("Error creating context", err) @@ -339,6 +354,12 @@ func InitCensus(nodeType NodeType, version string) { census.mSegmentClassProb = stats.Float64("segment_class_prob", "SegmentClassProb", "tot") census.mSceneClassification = stats.Int64("scene_classification_done", "SceneClassificationDone", "tot") + // Metrics for AI jobs + census.mAIModelsRequested = stats.Int64("ai_models_requested", "Number of AI models requested over time", "tot") + census.mAIRequestLatencyScore = stats.Float64("ai_request_latency_score", "AI request latency score, based on smallest pipeline unit", "") + census.mAIRequestPrice = stats.Float64("ai_request_price", "AI request price per unit, based on smallest pipeline unit", "") + census.mAIRequestError = stats.Int64("ai_request_errors", "Errors during AI request processing", "tot") + glog.Infof("Compiler: %s Arch %s OS %s Go version %s", runtime.Compiler, runtime.GOARCH, runtime.GOOS, runtime.Version()) glog.Infof("Livepeer version: %s", version) glog.Infof("Node type %s node ID %s", nodeType, NodeID) @@ -855,6 +876,36 @@ func InitCensus(nodeType NodeType, version string) { TagKeys: baseTags, Aggregation: view.Count(), }, + + // Metrics for AI jobs + { + Name: "ai_models_requested", + Measure: census.mAIModelsRequested, + Description: "Number of AI models requested over time", + TagKeys: append([]tag.Key{census.kPipeline, census.kModelName}, baseTags...), + Aggregation: view.Count(), + }, + { + Name: "ai_request_latency_score", + Measure: census.mAIRequestLatencyScore, + Description: "AI request latency score", + TagKeys: append([]tag.Key{census.kPipeline, census.kModelName}, baseTagsWithOrchInfo...), + Aggregation: view.LastValue(), + }, + { + Name: "ai_request_price", + Measure: census.mAIRequestPrice, + Description: "AI request price per unit", + TagKeys: append([]tag.Key{census.kPipeline, census.kModelName}, baseTags...), + Aggregation: view.LastValue(), + }, + { + Name: "ai_request_errors", + Measure: census.mAIRequestError, + Description: "Errors when processing AI requests", + TagKeys: append([]tag.Key{census.kErrorCode, census.kPipeline, census.kModelName}, baseTagsWithOrchInfo...), + Aggregation: view.Sum(), + }, } // Register the views @@ -1709,6 +1760,62 @@ func RewardCallError(sender string) { } } +// AIRequestFinished records gateway AI job request metrics. +func AIRequestFinished(ctx context.Context, pipeline string, model string, jobInfo AIJobInfo, orchInfo *lpnet.OrchestratorInfo) { + census.recordModelRequested(pipeline, model) + census.recordAIRequestLatencyScore(pipeline, model, jobInfo.LatencyScore, orchInfo) + census.recordAIRequestPricePerUnit(pipeline, model, jobInfo.PricePerUnit, orchInfo) +} + +// recordModelRequested increments request count for a specific AI model and pipeline. +func (cen *censusMetricsCounter) recordModelRequested(pipeline, modelName string) { + cen.lock.Lock() + defer cen.lock.Unlock() + + if err := stats.RecordWithTags(cen.ctx, + []tag.Mutator{tag.Insert(cen.kPipeline, pipeline), tag.Insert(cen.kModelName, modelName)}, cen.mAIModelsRequested.M(1)); err != nil { + glog.Errorf("Failed to record metrics with tags: %v", err) + } +} + +// recordAIRequestLatencyScore records the latency score for a AI job request. +func (cen *censusMetricsCounter) recordAIRequestLatencyScore(Pipeline string, Model string, latencyScore float64, orchInfo *lpnet.OrchestratorInfo) { + cen.lock.Lock() + defer cen.lock.Unlock() + + if err := stats.RecordWithTags(cen.ctx, + []tag.Mutator{tag.Insert(cen.kPipeline, Pipeline), tag.Insert(cen.kModelName, Model), tag.Insert(cen.kOrchestratorURI, orchInfo.GetTranscoder()), tag.Insert(cen.kOrchestratorAddress, common.BytesToAddress(orchInfo.GetAddress()).String())}, + cen.mAIRequestLatencyScore.M(latencyScore)); err != nil { + glog.Errorf("Error recording metrics err=%q", err) + } +} + +// recordAIRequestPricePerUnit records the price per unit for a AI job request. +func (cen *censusMetricsCounter) recordAIRequestPricePerUnit(Pipeline string, Model string, pricePerUnit float64, orchInfo *lpnet.OrchestratorInfo) { + cen.lock.Lock() + defer cen.lock.Unlock() + + if err := stats.RecordWithTags(cen.ctx, + []tag.Mutator{tag.Insert(cen.kPipeline, Pipeline), tag.Insert(cen.kModelName, Model), tag.Insert(cen.kOrchestratorURI, orchInfo.GetTranscoder()), tag.Insert(cen.kOrchestratorAddress, common.BytesToAddress(orchInfo.GetAddress()).String())}, + cen.mAIRequestPrice.M(pricePerUnit)); err != nil { + glog.Errorf("Error recording metrics err=%q", err) + } +} + +// AIRequestError logs an error in a gateway AI job request. +func AIRequestError(code string, Pipeline string, Model string, orchInfo *lpnet.OrchestratorInfo) { + orchAddr := "" + if addr := orchInfo.GetAddress(); addr != nil { + orchAddr = common.BytesToAddress(addr).String() + } + + if err := stats.RecordWithTags(census.ctx, + []tag.Mutator{tag.Insert(census.kErrorCode, code), tag.Insert(census.kPipeline, Pipeline), tag.Insert(census.kModelName, Model), tag.Insert(census.kOrchestratorURI, orchInfo.GetTranscoder()), tag.Insert(census.kOrchestratorAddress, orchAddr)}, + census.mAIRequestError.M(1)); err != nil { + glog.Errorf("Error recording metrics err=%q", err) + } +} + // Convert wei to gwei func wei2gwei(wei *big.Int) float64 { gwei, _ := new(big.Float).Quo(new(big.Float).SetInt(wei), big.NewFloat(float64(gweiConversionFactor))).Float64() diff --git a/server/ai_process.go b/server/ai_process.go index 0f5ab335f..14b746f7d 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -19,6 +19,7 @@ import ( "github.com/livepeer/go-livepeer/clog" "github.com/livepeer/go-livepeer/common" "github.com/livepeer/go-livepeer/core" + "github.com/livepeer/go-livepeer/monitor" "github.com/livepeer/go-tools/drivers" "github.com/livepeer/lpms/stream" ) @@ -85,7 +86,11 @@ func processTextToImage(ctx context.Context, params aiRequestParams, req worker. func submitTextToImage(ctx context.Context, params aiRequestParams, sess *AISession, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) { client, err := worker.NewClientWithResponses(sess.Transcoder(), worker.WithHTTPClient(httpClient)) + if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "text-to-image", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } @@ -101,6 +106,9 @@ func submitTextToImage(ctx context.Context, params aiRequestParams, sess *AISess outPixels := int64(*req.Height) * int64(*req.Width) setHeaders, balUpdate, err := prepareAIPayment(ctx, sess, outPixels) if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "text-to-image", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } defer completeBalanceUpdate(sess.BroadcastSession, balUpdate) @@ -109,6 +117,9 @@ func submitTextToImage(ctx context.Context, params aiRequestParams, sess *AISess resp, err := client.TextToImageWithResponse(ctx, req, setHeaders) took := time.Since(start) if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "text-to-image", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } @@ -140,6 +151,15 @@ func submitTextToImage(ctx context.Context, params aiRequestParams, sess *AISess sess.LatencyScore = took.Seconds() / float64(outPixels) / (numImages * numInferenceSteps) + if monitor.Enabled { + var pricePerAIUnit float64 + if priceInfo := sess.OrchestratorInfo.GetPriceInfo(); priceInfo != nil && priceInfo.PixelsPerUnit != 0 { + pricePerAIUnit = float64(priceInfo.PricePerUnit) / float64(priceInfo.PixelsPerUnit) + } + + monitor.AIRequestFinished(ctx, "text-to-image", *req.ModelId, monitor.AIJobInfo{LatencyScore: sess.LatencyScore, PricePerUnit: pricePerAIUnit}, sess.OrchestratorInfo) + } + return resp.JSON200, nil } @@ -178,26 +198,41 @@ func submitImageToImage(ctx context.Context, params aiRequestParams, sess *AISes var buf bytes.Buffer mw, err := worker.NewImageToImageMultipartWriter(&buf, req) if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "image-to-image", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } client, err := worker.NewClientWithResponses(sess.Transcoder(), worker.WithHTTPClient(httpClient)) if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "image-to-image", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } imageRdr, err := req.Image.Reader() if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "image-to-image", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } config, _, err := image.DecodeConfig(imageRdr) if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "image-to-image", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } outPixels := int64(config.Height) * int64(config.Width) setHeaders, balUpdate, err := prepareAIPayment(ctx, sess, outPixels) if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "image-to-image", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } defer completeBalanceUpdate(sess.BroadcastSession, balUpdate) @@ -206,6 +241,9 @@ func submitImageToImage(ctx context.Context, params aiRequestParams, sess *AISes resp, err := client.ImageToImageWithBodyWithResponse(ctx, mw.FormDataContentType(), &buf, setHeaders) took := time.Since(start) if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "image-to-image", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } @@ -237,6 +275,15 @@ func submitImageToImage(ctx context.Context, params aiRequestParams, sess *AISes sess.LatencyScore = took.Seconds() / float64(outPixels) / (numImages * numInferenceSteps) + if monitor.Enabled { + var pricePerAIUnit float64 + if priceInfo := sess.OrchestratorInfo.GetPriceInfo(); priceInfo != nil && priceInfo.PixelsPerUnit != 0 { + pricePerAIUnit = float64(priceInfo.PricePerUnit) / float64(priceInfo.PixelsPerUnit) + } + + monitor.AIRequestFinished(ctx, "image-to-image", *req.ModelId, monitor.AIJobInfo{LatencyScore: sess.LatencyScore, PricePerUnit: pricePerAIUnit}, sess.OrchestratorInfo) + } + return resp.JSON200, nil } @@ -280,11 +327,17 @@ func submitImageToVideo(ctx context.Context, params aiRequestParams, sess *AISes var buf bytes.Buffer mw, err := worker.NewImageToVideoMultipartWriter(&buf, req) if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "image-to-video", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } client, err := worker.NewClientWithResponses(sess.Transcoder(), worker.WithHTTPClient(httpClient)) if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "image-to-video", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } @@ -301,6 +354,9 @@ func submitImageToVideo(ctx context.Context, params aiRequestParams, sess *AISes outPixels := int64(*req.Height) * int64(*req.Width) * frames setHeaders, balUpdate, err := prepareAIPayment(ctx, sess, outPixels) if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "image-to-video", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } defer completeBalanceUpdate(sess.BroadcastSession, balUpdate) @@ -309,12 +365,18 @@ func submitImageToVideo(ctx context.Context, params aiRequestParams, sess *AISes resp, err := client.ImageToVideoWithBody(ctx, mw.FormDataContentType(), &buf, setHeaders) took := time.Since(start) if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "image-to-video", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } defer resp.Body.Close() data, err := io.ReadAll(resp.Body) if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "image-to-video", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } @@ -329,6 +391,9 @@ func submitImageToVideo(ctx context.Context, params aiRequestParams, sess *AISes var res worker.ImageResponse if err := json.Unmarshal(data, &res); err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "image-to-video", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } @@ -341,6 +406,15 @@ func submitImageToVideo(ctx context.Context, params aiRequestParams, sess *AISes } sess.LatencyScore = took.Seconds() / float64(outPixels) / numInferenceSteps + if monitor.Enabled { + var pricePerAIUnit float64 + if priceInfo := sess.OrchestratorInfo.GetPriceInfo(); priceInfo != nil && priceInfo.PixelsPerUnit != 0 { + pricePerAIUnit = float64(priceInfo.PricePerUnit) / float64(priceInfo.PixelsPerUnit) + } + + monitor.AIRequestFinished(ctx, "image-to-video", *req.ModelId, monitor.AIJobInfo{LatencyScore: sess.LatencyScore, PricePerUnit: pricePerAIUnit}, sess.OrchestratorInfo) + } + return &res, nil } @@ -379,20 +453,32 @@ func submitUpscale(ctx context.Context, params aiRequestParams, sess *AISession, var buf bytes.Buffer mw, err := worker.NewUpscaleMultipartWriter(&buf, req) if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "upscale", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } client, err := worker.NewClientWithResponses(sess.Transcoder(), worker.WithHTTPClient(httpClient)) if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "upscale", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } imageRdr, err := req.Image.Reader() if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "upscale", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } config, _, err := image.DecodeConfig(imageRdr) if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "upscale", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } outPixels := int64(config.Height) * int64(config.Width) @@ -407,6 +493,9 @@ func submitUpscale(ctx context.Context, params aiRequestParams, sess *AISession, resp, err := client.UpscaleWithBodyWithResponse(ctx, mw.FormDataContentType(), &buf, setHeaders) took := time.Since(start) if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "upscale", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } @@ -429,6 +518,15 @@ func submitUpscale(ctx context.Context, params aiRequestParams, sess *AISession, } sess.LatencyScore = took.Seconds() / float64(outPixels) / numInferenceSteps + if monitor.Enabled { + var pricePerAIUnit float64 + if priceInfo := sess.OrchestratorInfo.GetPriceInfo(); priceInfo != nil && priceInfo.PixelsPerUnit != 0 { + pricePerAIUnit = float64(priceInfo.PricePerUnit) / float64(priceInfo.PixelsPerUnit) + } + + monitor.AIRequestFinished(ctx, "upscale", *req.ModelId, monitor.AIJobInfo{LatencyScore: sess.LatencyScore, PricePerUnit: pricePerAIUnit}, sess.OrchestratorInfo) + } + return resp.JSON200, nil } @@ -554,6 +652,7 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface default: return nil, fmt.Errorf("unsupported request type %T", req) } + capName, _ := core.CapabilityToName(cap) var resp interface{} @@ -564,7 +663,11 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface for { select { case <-cctx.Done(): - return nil, &ServiceUnavailableError{err: fmt.Errorf("no orchestrators available within %v timeout", processingRetryTimeout)} + err := fmt.Errorf("no orchestrators available within %v timeout", processingRetryTimeout) + if monitor.Enabled { + monitor.AIRequestError(err.Error(), capName, modelID, nil) + } + return nil, &ServiceUnavailableError{err: err} default: } @@ -594,7 +697,11 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface } if resp == nil { - return nil, &ServiceUnavailableError{err: errors.New("no orchestrators available")} + errMsg := "no orchestrators available" + if monitor.Enabled { + monitor.AIRequestError(errMsg, capName, modelID, nil) + } + return nil, &ServiceUnavailableError{err: errors.New(errMsg)} } return resp, nil } @@ -627,11 +734,21 @@ func prepareAIPayment(ctx context.Context, sess *AISession, outPixels int64) (wo payment, err := genPayment(ctx, sess.BroadcastSession, balUpdate.NumTickets) if err != nil { + clog.Errorf(ctx, "Could not create payment err=%q", err) + + if monitor.Enabled { + monitor.PaymentCreateError(ctx) + } + return nil, nil, err } // As soon as the request is sent to the orch consider the balance update's credit as spent balUpdate.Status = CreditSpent + if monitor.Enabled { + monitor.TicketValueSent(ctx, balUpdate.NewCredit) + monitor.TicketsSent(ctx, balUpdate.NumTickets) + } setHeaders := func(_ context.Context, req *http.Request) error { req.Header.Set(segmentHeader, segCreds) From 82292c938280b5bbe3a047b43bb0768905dfef0e Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 18 Jul 2024 13:44:15 +0200 Subject: [PATCH 136/203] feat(ai): add A2T gateway metrics (#3100) This commit adds the gateway metrics to the Audio-to-text pipeline. --- server/ai_process.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/server/ai_process.go b/server/ai_process.go index 14b746f7d..613c29424 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -485,6 +485,9 @@ func submitUpscale(ctx context.Context, params aiRequestParams, sess *AISession, setHeaders, balUpdate, err := prepareAIPayment(ctx, sess, outPixels) if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "upscale", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } defer completeBalanceUpdate(sess.BroadcastSession, balUpdate) @@ -534,22 +537,34 @@ func submitAudioToText(ctx context.Context, params aiRequestParams, sess *AISess var buf bytes.Buffer mw, err := worker.NewAudioToTextMultipartWriter(&buf, req) if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "audio-to-text", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } client, err := worker.NewClientWithResponses(sess.Transcoder(), worker.WithHTTPClient(httpClient)) if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "audio-to-text", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } durationSeconds, err := common.CalculateAudioDuration(req.Audio) if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "audio-to-text", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } clog.V(common.VERBOSE).Infof(ctx, "Submitting audio-to-text media with duration: %d seconds", durationSeconds) setHeaders, balUpdate, err := prepareAIPayment(ctx, sess, durationSeconds*1000) if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "audio-to-text", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } defer completeBalanceUpdate(sess.BroadcastSession, balUpdate) @@ -558,12 +573,18 @@ func submitAudioToText(ctx context.Context, params aiRequestParams, sess *AISess resp, err := client.AudioToTextWithBody(ctx, mw.FormDataContentType(), &buf, setHeaders) took := time.Since(start) if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "audio-to-text", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } defer resp.Body.Close() data, err := io.ReadAll(resp.Body) if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "audio-to-text", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } @@ -578,12 +599,24 @@ func submitAudioToText(ctx context.Context, params aiRequestParams, sess *AISess var res worker.TextResponse if err := json.Unmarshal(data, &res); err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "audio-to-text", *req.ModelId, sess.OrchestratorInfo) + } return nil, err } // TODO: Refine this rough estimate in future iterations sess.LatencyScore = took.Seconds() / float64(durationSeconds) + if monitor.Enabled { + var pricePerAIUnit float64 + if priceInfo := sess.OrchestratorInfo.GetPriceInfo(); priceInfo != nil && priceInfo.PixelsPerUnit != 0 { + pricePerAIUnit = float64(priceInfo.PricePerUnit) / float64(priceInfo.PixelsPerUnit) + } + + monitor.AIRequestFinished(ctx, "audio-to-text", *req.ModelId, monitor.AIJobInfo{LatencyScore: sess.LatencyScore, PricePerUnit: pricePerAIUnit}, sess.OrchestratorInfo) + } + return &res, nil } From 5aadffb9fdddd88aaf94715c28586e9b65e4d05b Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 18 Jul 2024 15:40:47 +0200 Subject: [PATCH 137/203] feat(ai): add AI orchestrator metrics (#3097) * Add gateway metric for roundtrip ai times by model and pipeline * Rename metrics and add unique manifest * Fix name mismatch * modelsRequested not working correctly * feat: add initial POC AI gateway metrics This commit adds the initial AI gateway metrics so that they can reviewed by others. The code still need to be cleaned up and the buckets adjusted. * feat: improve AI metrics This commit improves the AI metrics so that they are easier to work with. * feat(ai): log no capacity error to metrics This commit ensures that an error is logged when the Gateway could not find orchestrators for a given model and capability. * feat(ai): add TicketValueSent and TicketsSent metrics This commit ensure that the `ticket_value_sent` abd `tickets_sent` metrics are also created for a AI Gateway. * fix(ai): ensure that AI metrics have orch address label This commit ensures that the AI gateway metrics contain the orch address label. * feat(ai): add orchestrator AI census metrics This commit introduces a suite of AI orchestrator metrics to the census module, mirroring those received by the Gateway. The newly added metrics include `ai_models_requested`, `ai_request_latency_score`, `ai_request_price`, and `ai_request_errors`, facilitating comprehensive tracking and analysis of AI request handling performance on the orchestrator side. * refactor: improve orchestrator metrics tags This commit ensures that the right tags are attached to the Orchestrator AI metrics. * refactor(ai): improve latency score calculations This commit ensures that no devide by zero errors can occur in the latency score calculations. --------- Co-authored-by: Elite Encoder --- monitor/census.go | 74 ++++++++++++++++--- server/ai_http.go | 30 ++++++++ server/ai_process.go | 164 ++++++++++++++++++++++++++++--------------- 3 files changed, 199 insertions(+), 69 deletions(-) diff --git a/monitor/census.go b/monitor/census.go index 373220122..2f883d5e4 100644 --- a/monitor/census.go +++ b/monitor/census.go @@ -380,6 +380,7 @@ func InitCensus(nodeType NodeType, version string) { baseTagsWithEthAddr := baseTags baseTagsWithManifestIDAndEthAddr := baseTags baseTagsWithOrchInfo := baseTags + baseTagsWithGatewayInfo := baseTags if PerStreamMetrics { baseTagsWithManifestID = []tag.Key{census.kNodeID, census.kNodeType, census.kManifestID} baseTagsWithEthAddr = []tag.Key{census.kNodeID, census.kNodeType, census.kSender} @@ -391,8 +392,19 @@ func InitCensus(nodeType NodeType, version string) { } baseTagsWithManifestIDAndOrchInfo := baseTagsWithManifestID baseTagsWithOrchInfo = append([]tag.Key{census.kOrchestratorURI, census.kOrchestratorAddress}, baseTags...) + baseTagsWithGatewayInfo = append([]tag.Key{census.kSender}, baseTags...) baseTagsWithManifestIDAndOrchInfo = append([]tag.Key{census.kOrchestratorURI, census.kOrchestratorAddress}, baseTagsWithManifestID...) + // Add node type specific tags. + baseTagsWithNodeInfo := baseTags + aiRequestLatencyScoreTags := baseTags + if nodeType == Orchestrator { + baseTagsWithNodeInfo = baseTagsWithGatewayInfo + } else { + baseTagsWithNodeInfo = baseTagsWithOrchInfo + aiRequestLatencyScoreTags = baseTagsWithOrchInfo + } + views := []*view.View{ { Name: "versions", @@ -889,7 +901,7 @@ func InitCensus(nodeType NodeType, version string) { Name: "ai_request_latency_score", Measure: census.mAIRequestLatencyScore, Description: "AI request latency score", - TagKeys: append([]tag.Key{census.kPipeline, census.kModelName}, baseTagsWithOrchInfo...), + TagKeys: append([]tag.Key{census.kPipeline, census.kModelName}, aiRequestLatencyScoreTags...), Aggregation: view.LastValue(), }, { @@ -903,7 +915,7 @@ func InitCensus(nodeType NodeType, version string) { Name: "ai_request_errors", Measure: census.mAIRequestError, Description: "Errors when processing AI requests", - TagKeys: append([]tag.Key{census.kErrorCode, census.kPipeline, census.kModelName}, baseTagsWithOrchInfo...), + TagKeys: append([]tag.Key{census.kErrorCode, census.kPipeline, census.kModelName}, baseTagsWithNodeInfo...), Aggregation: view.Sum(), }, } @@ -1760,13 +1772,6 @@ func RewardCallError(sender string) { } } -// AIRequestFinished records gateway AI job request metrics. -func AIRequestFinished(ctx context.Context, pipeline string, model string, jobInfo AIJobInfo, orchInfo *lpnet.OrchestratorInfo) { - census.recordModelRequested(pipeline, model) - census.recordAIRequestLatencyScore(pipeline, model, jobInfo.LatencyScore, orchInfo) - census.recordAIRequestPricePerUnit(pipeline, model, jobInfo.PricePerUnit, orchInfo) -} - // recordModelRequested increments request count for a specific AI model and pipeline. func (cen *censusMetricsCounter) recordModelRequested(pipeline, modelName string) { cen.lock.Lock() @@ -1778,6 +1783,13 @@ func (cen *censusMetricsCounter) recordModelRequested(pipeline, modelName string } } +// AIRequestFinished records gateway AI job request metrics. +func AIRequestFinished(ctx context.Context, pipeline string, model string, jobInfo AIJobInfo, orchInfo *lpnet.OrchestratorInfo) { + census.recordModelRequested(pipeline, model) + census.recordAIRequestLatencyScore(pipeline, model, jobInfo.LatencyScore, orchInfo) + census.recordAIRequestPricePerUnit(pipeline, model, jobInfo.PricePerUnit) +} + // recordAIRequestLatencyScore records the latency score for a AI job request. func (cen *censusMetricsCounter) recordAIRequestLatencyScore(Pipeline string, Model string, latencyScore float64, orchInfo *lpnet.OrchestratorInfo) { cen.lock.Lock() @@ -1791,12 +1803,12 @@ func (cen *censusMetricsCounter) recordAIRequestLatencyScore(Pipeline string, Mo } // recordAIRequestPricePerUnit records the price per unit for a AI job request. -func (cen *censusMetricsCounter) recordAIRequestPricePerUnit(Pipeline string, Model string, pricePerUnit float64, orchInfo *lpnet.OrchestratorInfo) { +func (cen *censusMetricsCounter) recordAIRequestPricePerUnit(Pipeline string, Model string, pricePerUnit float64) { cen.lock.Lock() defer cen.lock.Unlock() if err := stats.RecordWithTags(cen.ctx, - []tag.Mutator{tag.Insert(cen.kPipeline, Pipeline), tag.Insert(cen.kModelName, Model), tag.Insert(cen.kOrchestratorURI, orchInfo.GetTranscoder()), tag.Insert(cen.kOrchestratorAddress, common.BytesToAddress(orchInfo.GetAddress()).String())}, + []tag.Mutator{tag.Insert(cen.kPipeline, Pipeline), tag.Insert(cen.kModelName, Model)}, cen.mAIRequestPrice.M(pricePerUnit)); err != nil { glog.Errorf("Error recording metrics err=%q", err) } @@ -1816,6 +1828,46 @@ func AIRequestError(code string, Pipeline string, Model string, orchInfo *lpnet. } } +// AIJobProcessed records orchestrator AI job processing metrics. +func AIJobProcessed(ctx context.Context, pipeline string, model string, jobInfo AIJobInfo) { + census.recordModelRequested(pipeline, model) + census.recordAIJobLatencyScore(pipeline, model, jobInfo.LatencyScore) + census.recordAIJobPricePerUnit(pipeline, model, jobInfo.PricePerUnit) +} + +// recordAIJobLatencyScore records the latency score for a processed AI job. +func (cen *censusMetricsCounter) recordAIJobLatencyScore(Pipeline string, Model string, latencyScore float64) { + cen.lock.Lock() + defer cen.lock.Unlock() + + if err := stats.RecordWithTags(cen.ctx, + []tag.Mutator{tag.Insert(cen.kPipeline, Pipeline), tag.Insert(cen.kModelName, Model)}, + cen.mAIRequestLatencyScore.M(latencyScore)); err != nil { + glog.Errorf("Error recording metrics err=%q", err) + } +} + +// recordAIJobPricePerUnit logs the cost per unit of a processed AI job. +func (cen *censusMetricsCounter) recordAIJobPricePerUnit(Pipeline string, Model string, pricePerUnit float64) { + cen.lock.Lock() + defer cen.lock.Unlock() + + if err := stats.RecordWithTags(cen.ctx, + []tag.Mutator{tag.Insert(cen.kPipeline, Pipeline), tag.Insert(cen.kModelName, Model)}, + cen.mAIRequestPrice.M(pricePerUnit)); err != nil { + glog.Errorf("Error recording metrics err=%q", err) + } +} + +// AIProcessingError logs errors in orchestrator AI job processing. +func AIProcessingError(code string, Pipeline string, Model string, sender string) { + if err := stats.RecordWithTags(census.ctx, + []tag.Mutator{tag.Insert(census.kErrorCode, code), tag.Insert(census.kPipeline, Pipeline), tag.Insert(census.kModelName, Model), tag.Insert(census.kSender, sender)}, + census.mAIRequestError.M(1)); err != nil { + glog.Errorf("Error recording metrics err=%q", err) + } +} + // Convert wei to gwei func wei2gwei(wei *big.Int) float64 { gwei, _ := new(big.Float).Quo(new(big.Float).SetInt(wei), big.NewFloat(float64(gweiConversionFactor))).Float64() diff --git a/server/ai_http.go b/server/ai_http.go index 4ef6eac54..eba099df3 100644 --- a/server/ai_http.go +++ b/server/ai_http.go @@ -14,6 +14,7 @@ import ( "github.com/livepeer/go-livepeer/clog" "github.com/livepeer/go-livepeer/common" "github.com/livepeer/go-livepeer/core" + "github.com/livepeer/go-livepeer/monitor" middleware "github.com/oapi-codegen/nethttp-middleware" "github.com/oapi-codegen/runtime" ) @@ -309,6 +310,9 @@ func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request start := time.Now() resp, err := submitFn(ctx) if err != nil { + if monitor.Enabled { + monitor.AIProcessingError(err.Error(), pipeline, modelID, sender.Hex()) + } respondWithError(w, err.Error(), http.StatusInternalServerError) return } @@ -321,6 +325,32 @@ func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request // If additional parameters that influence compute cost become configurable, then the formula should be reconsidered orch.DebitFees(sender, manifestID, payment.GetExpectedPrice(), outPixels) + if monitor.Enabled { + var latencyScore float64 + switch v := req.(type) { + case worker.TextToImageJSONRequestBody: + latencyScore = CalculateTextToImageLatencyScore(took, v, outPixels) + case worker.ImageToImageMultipartRequestBody: + latencyScore = CalculateImageToImageLatencyScore(took, v, outPixels) + case worker.ImageToVideoMultipartRequestBody: + latencyScore = CalculateImageToVideoLatencyScore(took, v, outPixels) + case worker.UpscaleMultipartRequestBody: + latencyScore = CalculateUpscaleLatencyScore(took, v, outPixels) + case worker.AudioToTextMultipartRequestBody: + durationSeconds, err := common.CalculateAudioDuration(v.Audio) + if err == nil { + latencyScore = CalculateAudioToTextLatencyScore(took, durationSeconds) + } + } + + var pricePerAIUnit float64 + if priceInfo := payment.GetExpectedPrice(); priceInfo != nil && priceInfo.GetPixelsPerUnit() != 0 { + pricePerAIUnit = float64(priceInfo.GetPricePerUnit()) / float64(priceInfo.GetPixelsPerUnit()) + } + + monitor.AIJobProcessed(ctx, pipeline, modelID, monitor.AIJobInfo{LatencyScore: latencyScore, PricePerUnit: pricePerAIUnit}) + } + w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _ = json.NewEncoder(w).Encode(resp) diff --git a/server/ai_process.go b/server/ai_process.go index 613c29424..d28658221 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -9,6 +9,7 @@ import ( "fmt" "image" "io" + "math" "math/big" "net/http" "path/filepath" @@ -53,6 +54,30 @@ type aiRequestParams struct { sessManager *AISessionManager } +// CalculateTextToImageLatencyScore computes the time taken per pixel for an text-to-image request. +func CalculateTextToImageLatencyScore(took time.Duration, req worker.TextToImageJSONRequestBody, outPixels int64) float64 { + if outPixels <= 0 { + return 0 + } + + // TODO: Default values for the number of images and inference steps are currently hardcoded. + // These should be managed by the nethttpmiddleware. Refer to issue LIV-412 for more details. + numImages := float64(1) + if req.NumImagesPerPrompt != nil { + numImages = math.Max(1, float64(*req.NumImagesPerPrompt)) + } + numInferenceSteps := float64(50) + if req.NumInferenceSteps != nil { + numInferenceSteps = math.Max(1, float64(*req.NumInferenceSteps)) + } + // Handle special case for SDXL-Lightning model. + if strings.HasPrefix(*req.ModelId, "ByteDance/SDXL-Lightning") { + numInferenceSteps = math.Max(1, core.ParseStepsFromModelID(req.ModelId, 8)) + } + + return took.Seconds() / float64(outPixels) / (numImages * numInferenceSteps) +} + func processTextToImage(ctx context.Context, params aiRequestParams, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) { resp, err := processAIRequest(ctx, params, req) if err != nil { @@ -134,22 +159,7 @@ func submitTextToImage(ctx context.Context, params aiRequestParams, sess *AISess } // TODO: Refine this rough estimate in future iterations. - // TODO: Default values for the number of images and inference steps are currently hardcoded. - // These should be managed by the nethttpmiddleware. Refer to issue LIV-412 for more details. - numImages := float64(1) - if req.NumImagesPerPrompt != nil { - numImages = float64(*req.NumImagesPerPrompt) - } - numInferenceSteps := float64(50) - if req.NumInferenceSteps != nil { - numInferenceSteps = float64(*req.NumInferenceSteps) - } - // Handle special case for SDXL-Lightning model. - if strings.HasPrefix(*req.ModelId, "ByteDance/SDXL-Lightning") { - numInferenceSteps = core.ParseStepsFromModelID(req.ModelId, 8) - } - - sess.LatencyScore = took.Seconds() / float64(outPixels) / (numImages * numInferenceSteps) + sess.LatencyScore = CalculateTextToImageLatencyScore(took, req, outPixels) if monitor.Enabled { var pricePerAIUnit float64 @@ -163,6 +173,30 @@ func submitTextToImage(ctx context.Context, params aiRequestParams, sess *AISess return resp.JSON200, nil } +// CalculateImageToImageLatencyScore computes the time taken per pixel for an image-to-image request. +func CalculateImageToImageLatencyScore(took time.Duration, req worker.ImageToImageMultipartRequestBody, outPixels int64) float64 { + if outPixels <= 0 { + return 0 + } + + // TODO: Default values for the number of images and inference steps are currently hardcoded. + // These should be managed by the nethttpmiddleware. Refer to issue LIV-412 for more details. + numImages := float64(1) + if req.NumImagesPerPrompt != nil { + numImages = math.Max(1, float64(*req.NumImagesPerPrompt)) + } + numInferenceSteps := float64(100) + if req.NumInferenceSteps != nil { + numInferenceSteps = math.Max(1, float64(*req.NumInferenceSteps)) + } + // Handle special case for SDXL-Lightning model. + if strings.HasPrefix(*req.ModelId, "ByteDance/SDXL-Lightning") { + numInferenceSteps = math.Max(1, core.ParseStepsFromModelID(req.ModelId, 8)) + } + + return took.Seconds() / float64(outPixels) / (numImages * numInferenceSteps) +} + func processImageToImage(ctx context.Context, params aiRequestParams, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { resp, err := processAIRequest(ctx, params, req) if err != nil { @@ -258,22 +292,7 @@ func submitImageToImage(ctx context.Context, params aiRequestParams, sess *AISes } // TODO: Refine this rough estimate in future iterations. - // TODO: Default values for the number of images and inference steps are currently hardcoded. - // These should be managed by the nethttpmiddleware. Refer to issue LIV-412 for more details. - numImages := float64(1) - if req.NumImagesPerPrompt != nil { - numImages = float64(*req.NumImagesPerPrompt) - } - numInferenceSteps := float64(100) - if req.NumInferenceSteps != nil { - numInferenceSteps = float64(*req.NumInferenceSteps) - } - // Handle special case for SDXL-Lightning model. - if strings.HasPrefix(*req.ModelId, "ByteDance/SDXL-Lightning") { - numInferenceSteps = core.ParseStepsFromModelID(req.ModelId, 8) - } - - sess.LatencyScore = took.Seconds() / float64(outPixels) / (numImages * numInferenceSteps) + sess.LatencyScore = CalculateImageToImageLatencyScore(took, req, outPixels) if monitor.Enabled { var pricePerAIUnit float64 @@ -287,6 +306,22 @@ func submitImageToImage(ctx context.Context, params aiRequestParams, sess *AISes return resp.JSON200, nil } +// CalculateImageToVideoLatencyScore computes the time taken per pixel for an image-to-video request. +func CalculateImageToVideoLatencyScore(took time.Duration, req worker.ImageToVideoMultipartRequestBody, outPixels int64) float64 { + if outPixels <= 0 { + return 0 + } + + // TODO: Default values for the number of inference steps is currently hardcoded. + // These should be managed by the nethttpmiddleware. Refer to issue LIV-412 for more details. + numInferenceSteps := float64(25) + if req.NumInferenceSteps != nil { + numInferenceSteps = math.Max(1, float64(*req.NumInferenceSteps)) + } + + return took.Seconds() / float64(outPixels) / numInferenceSteps +} + func processImageToVideo(ctx context.Context, params aiRequestParams, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { resp, err := processAIRequest(ctx, params, req) if err != nil { @@ -398,13 +433,7 @@ func submitImageToVideo(ctx context.Context, params aiRequestParams, sess *AISes } // TODO: Refine this rough estimate in future iterations - // TODO: Default values for the number of inference steps is currently hardcoded. - // These should be managed by the nethttpmiddleware. Refer to issue LIV-412 for more details. - numInferenceSteps := float64(25) - if req.NumInferenceSteps != nil { - numInferenceSteps = float64(*req.NumInferenceSteps) - } - sess.LatencyScore = took.Seconds() / float64(outPixels) / numInferenceSteps + sess.LatencyScore = CalculateImageToVideoLatencyScore(took, req, outPixels) if monitor.Enabled { var pricePerAIUnit float64 @@ -418,6 +447,22 @@ func submitImageToVideo(ctx context.Context, params aiRequestParams, sess *AISes return &res, nil } +// CalculateUpscaleLatencyScore computes the time taken per pixel for an upscale request. +func CalculateUpscaleLatencyScore(took time.Duration, req worker.UpscaleMultipartRequestBody, outPixels int64) float64 { + if outPixels <= 0 { + return 0 + } + + // TODO: Default values for the number of inference steps is currently hardcoded. + // These should be managed by the nethttpmiddleware. Refer to issue LIV-412 for more details. + numInferenceSteps := float64(75) + if req.NumInferenceSteps != nil { + numInferenceSteps = math.Max(1, float64(*req.NumInferenceSteps)) + } + + return took.Seconds() / float64(outPixels) / numInferenceSteps +} + func processUpscale(ctx context.Context, params aiRequestParams, req worker.UpscaleMultipartRequestBody) (*worker.ImageResponse, error) { resp, err := processAIRequest(ctx, params, req) if err != nil { @@ -513,13 +558,7 @@ func submitUpscale(ctx context.Context, params aiRequestParams, sess *AISession, } // TODO: Refine this rough estimate in future iterations - // TODO: Default values for the number of inference steps is currently hardcoded. - // These should be managed by the nethttpmiddleware. Refer to issue LIV-412 for more details. - numInferenceSteps := float64(75) - if req.NumInferenceSteps != nil { - numInferenceSteps = float64(*req.NumInferenceSteps) - } - sess.LatencyScore = took.Seconds() / float64(outPixels) / numInferenceSteps + sess.LatencyScore = CalculateUpscaleLatencyScore(took, req, outPixels) if monitor.Enabled { var pricePerAIUnit float64 @@ -533,6 +572,26 @@ func submitUpscale(ctx context.Context, params aiRequestParams, sess *AISession, return resp.JSON200, nil } +// CalculateAudioToTextLatencyScore computes the time taken per second of audio for an audio-to-text request. +func CalculateAudioToTextLatencyScore(took time.Duration, durationSeconds int64) float64 { + if durationSeconds <= 0 { + return 0 + } + + return took.Seconds() / float64(durationSeconds) +} + +func processAudioToText(ctx context.Context, params aiRequestParams, req worker.AudioToTextMultipartRequestBody) (*worker.TextResponse, error) { + resp, err := processAIRequest(ctx, params, req) + if err != nil { + return nil, err + } + + txtResp := resp.(*worker.TextResponse) + + return txtResp, nil +} + func submitAudioToText(ctx context.Context, params aiRequestParams, sess *AISession, req worker.AudioToTextMultipartRequestBody) (*worker.TextResponse, error) { var buf bytes.Buffer mw, err := worker.NewAudioToTextMultipartWriter(&buf, req) @@ -606,7 +665,7 @@ func submitAudioToText(ctx context.Context, params aiRequestParams, sess *AISess } // TODO: Refine this rough estimate in future iterations - sess.LatencyScore = took.Seconds() / float64(durationSeconds) + sess.LatencyScore = CalculateAudioToTextLatencyScore(took, durationSeconds) if monitor.Enabled { var pricePerAIUnit float64 @@ -620,17 +679,6 @@ func submitAudioToText(ctx context.Context, params aiRequestParams, sess *AISess return &res, nil } -func processAudioToText(ctx context.Context, params aiRequestParams, req worker.AudioToTextMultipartRequestBody) (*worker.TextResponse, error) { - resp, err := processAIRequest(ctx, params, req) - if err != nil { - return nil, err - } - - txtResp := resp.(*worker.TextResponse) - - return txtResp, nil -} - func processAIRequest(ctx context.Context, params aiRequestParams, req interface{}) (interface{}, error) { var cap core.Capability var modelID string From 87c24a2b37c79700dacd2c9480ffe39707414132 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Wed, 24 Jul 2024 10:20:17 +0200 Subject: [PATCH 138/203] ci(ai): improve ci comments This commit applies some small comment changes to ease the conflicts between the main and ai-video branch. --- .github/workflows/docker.yaml | 6 ++++-- .github/workflows/pr-labeler.yml | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 7f6928ae6..51be5052e 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -90,7 +90,8 @@ jobs: build-args: | BUILD_TAGS=${{ steps.build-tag.outputs.build-tags }} context: . - platforms: linux/amd64 + platforms: linux/amd64, linux/arm64 + # platforms: linux/amd64 push: true tags: ${{ steps.meta.outputs.tags }} file: "docker/Dockerfile" @@ -178,7 +179,8 @@ jobs: build-args: | BUILD_TAGS=${{ steps.build-tag.outputs.build-tags }} context: . - platforms: linux/amd64 + platforms: linux/amd64, linux/arm64 + # platforms: linux/amd64 push: true tags: ${{ steps.meta-builder.outputs.tags }} file: "docker/Dockerfile" diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml index 6bb3aa685..26bade1df 100644 --- a/.github/workflows/pr-labeler.yml +++ b/.github/workflows/pr-labeler.yml @@ -1,4 +1,4 @@ -name: Label issues +name: Label PRs on: pull_request_target: types: [opened, reopened] From 21f98a504590a06009c429621ccb81c718b4d368 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Sat, 27 Jul 2024 11:31:26 +0200 Subject: [PATCH 139/203] chore(ai): ensure ai-video-rebased ffmpeg file is used Since the lpms `ai-video` and `ai-video-rebase-main` branches are not yet merged into the main branch we need to ensure the right AI install_ffmpeg.sh script is used. --- install_ffmpeg.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install_ffmpeg.sh b/install_ffmpeg.sh index fdc111f8b..3de15fdc1 100755 --- a/install_ffmpeg.sh +++ b/install_ffmpeg.sh @@ -1,3 +1,3 @@ #!/usr/bin/env bash echo 'WARNING: downloading and executing lpms/install_ffmpeg.sh, use it directly in case of issues' -curl https://raw.githubusercontent.com/livepeer/lpms/d9c78b62effdb4f5c8fc438b6033da1090d04a03/install_ffmpeg.sh | bash -s $1 +curl https://raw.githubusercontent.com/livepeer/lpms/5b7b9f5e831f041c6cf707bbaad7b5503c2f138d/install_ffmpeg.sh | bash -s $1 From 280b4a54a9bee530e490b2b9ee73f063c83b09ee Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Sat, 27 Jul 2024 11:35:29 +0200 Subject: [PATCH 140/203] chore(ai): remove local go module dependency This commit removes the local go module dependency to lpms that was accidentally commited. --- go.mod | 2 -- 1 file changed, 2 deletions(-) diff --git a/go.mod b/go.mod index 082f4b50a..16051cab5 100644 --- a/go.mod +++ b/go.mod @@ -240,5 +240,3 @@ require ( lukechampine.com/blake3 v1.2.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect ) - -replace github.com/livepeer/lpms => /home/ricks/development/work/livepeer/ai_spe/lpms From 30641d23d337678e1e4c2baf48373797fe56bea7 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Mon, 29 Jul 2024 13:12:50 +0200 Subject: [PATCH 141/203] test: fix capability tests (#3107) This commit ensures that the stubOS struct is in-line with the latest [livepeer/go-tools](https://github.com/livepeer/go-tools/) version. --- core/capabilities_test.go | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/core/capabilities_test.go b/core/capabilities_test.go index c9c1ee319..4f29c6c61 100644 --- a/core/capabilities_test.go +++ b/core/capabilities_test.go @@ -382,26 +382,35 @@ type stubOS struct { storageType int32 } +func (os *stubOS) OS() drivers.OSDriver { + return nil +} +func (os *stubOS) SaveData(context.Context, string, io.Reader, *drivers.FileProperties, time.Duration) (string, error) { + return "", nil +} +func (os *stubOS) EndSession() {} func (os *stubOS) GetInfo() *drivers.OSInfo { if os.storageType == stubOSMagic { return nil } return &drivers.OSInfo{StorageType: drivers.OSInfo_StorageType(os.storageType)} } -func (os *stubOS) EndSession() {} -func (os *stubOS) SaveData(context.Context, string, io.Reader, map[string]string, time.Duration) (string, error) { - return "", nil -} func (os *stubOS) IsExternal() bool { return false } func (os *stubOS) IsOwn(url string) bool { return true } func (os *stubOS) ListFiles(ctx context.Context, prefix, delim string) (drivers.PageInfo, error) { return nil, nil } +func (os *stubOS) DeleteFile(ctx context.Context, name string) error { + return nil +} func (os *stubOS) ReadData(ctx context.Context, name string) (*drivers.FileInfoReader, error) { return nil, nil } -func (os *stubOS) OS() drivers.OSDriver { - return nil +func (os *stubOS) ReadDataRange(ctx context.Context, name, byteRange string) (*drivers.FileInfoReader, error) { + return nil, nil +} +func (os *stubOS) Presign(name string, expire time.Duration) (string, error) { + return "", nil } func TestCapability_StorageToCapability(t *testing.T) { From 01fc75be2d91dd40e91ba9c41c29d07123b72a0f Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Mon, 29 Jul 2024 14:42:07 +0200 Subject: [PATCH 142/203] test: fix broadcast test This commit fixes the bugs that were introduced by the AI codebas einto the broadcast test functions. --- server/broadcast_test.go | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/server/broadcast_test.go b/server/broadcast_test.go index 669ff0631..c219a192b 100644 --- a/server/broadcast_test.go +++ b/server/broadcast_test.go @@ -182,7 +182,10 @@ type stubOSSession struct { err error } -func (s *stubOSSession) SaveData(ctx context.Context, name string, data io.Reader, meta map[string]string, timeout time.Duration) (string, error) { +func (s *stubOSSession) OS() drivers.OSDriver { + return nil +} +func (s *stubOSSession) SaveData(ctx context.Context, name string, data io.Reader, meta *drivers.FileProperties, timeout time.Duration) (string, error) { s.saved = append(s.saved, name) return "saved_" + name, s.err } @@ -200,11 +203,17 @@ func (s *stubOSSession) IsOwn(url string) bool { func (s *stubOSSession) ListFiles(ctx context.Context, prefix, delim string) (drivers.PageInfo, error) { return nil, nil } +func (os *stubOSSession) DeleteFile(ctx context.Context, name string) error { + return nil +} func (s *stubOSSession) ReadData(ctx context.Context, name string) (*drivers.FileInfoReader, error) { return nil, nil } -func (s *stubOSSession) OS() drivers.OSDriver { - return nil +func (os *stubOSSession) ReadDataRange(ctx context.Context, name, byteRange string) (*drivers.FileInfoReader, error) { + return nil, nil +} +func (os *stubOSSession) Presign(name string, expire time.Duration) (string, error) { + return "", nil } type stubPlaylistManager struct { @@ -385,7 +394,7 @@ func TestSelectSession_MultipleInFlight2(t *testing.T) { defer func() { getOrchestratorInfoRPC = oldGetOrchestratorInfoRPC }() orchInfoCalled := 0 - getOrchestratorInfoRPC = func(ctx context.Context, bcast common.Broadcaster, orchestratorServer *url.URL) (*net.OrchestratorInfo, error) { + getOrchestratorInfoRPC = func(ctx context.Context, bcast common.Broadcaster, orchestratorServer *url.URL, caps *net.Capabilities) (*net.OrchestratorInfo, error) { orchInfoCalled++ return successOrchInfoUpdate, nil } @@ -604,7 +613,7 @@ func TestTranscodeSegment_RefreshSession(t *testing.T) { oldGetOrchestratorInfoRPC := getOrchestratorInfoRPC defer func() { getOrchestratorInfoRPC = oldGetOrchestratorInfoRPC }() - getOrchestratorInfoRPC = func(ctx context.Context, bcast common.Broadcaster, orchestratorServer *url.URL) (*net.OrchestratorInfo, error) { + getOrchestratorInfoRPC = func(ctx context.Context, bcast common.Broadcaster, orchestratorServer *url.URL, caps *net.Capabilities) (*net.OrchestratorInfo, error) { return successOrchInfoUpdate, nil } @@ -1503,7 +1512,7 @@ func TestRefreshSession(t *testing.T) { assert.Contains(err.Error(), "invalid control character in URL") // trigger getOrchestratorInfo error - getOrchestratorInfoRPC = func(ctx context.Context, bcast common.Broadcaster, orchestratorServer *url.URL) (*net.OrchestratorInfo, error) { + getOrchestratorInfoRPC = func(ctx context.Context, bcast common.Broadcaster, orchestratorServer *url.URL, caps *net.Capabilities) (*net.OrchestratorInfo, error) { return nil, errors.New("some error") } sess = StubBroadcastSession("foo") @@ -1511,7 +1520,7 @@ func TestRefreshSession(t *testing.T) { assert.EqualError(err, "some error") // trigger update - getOrchestratorInfoRPC = func(ctx context.Context, bcast common.Broadcaster, orchestratorServer *url.URL) (*net.OrchestratorInfo, error) { + getOrchestratorInfoRPC = func(ctx context.Context, bcast common.Broadcaster, orchestratorServer *url.URL, caps *net.Capabilities) (*net.OrchestratorInfo, error) { return successOrchInfoUpdate, nil } err = refreshSession(context.TODO(), sess) @@ -1522,7 +1531,7 @@ func TestRefreshSession(t *testing.T) { oldRefreshTimeout := refreshTimeout defer func() { refreshTimeout = oldRefreshTimeout }() refreshTimeout = 10 * time.Millisecond - getOrchestratorInfoRPC = func(ctx context.Context, bcast common.Broadcaster, serv *url.URL) (*net.OrchestratorInfo, error) { + getOrchestratorInfoRPC = func(ctx context.Context, bcast common.Broadcaster, serv *url.URL, caps *net.Capabilities) (*net.OrchestratorInfo, error) { // Wait until the refreshTimeout has elapsed select { case <-ctx.Done(): From ed8c0a4f7d0785ce8f157aba9c841209fbd72519 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Mon, 29 Jul 2024 14:42:07 +0200 Subject: [PATCH 143/203] test: fix broadcast test This commit fixes the bugs that were introduced by the AI codebas einto the broadcast test functions. --- server/broadcast_test.go | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/server/broadcast_test.go b/server/broadcast_test.go index 669ff0631..c219a192b 100644 --- a/server/broadcast_test.go +++ b/server/broadcast_test.go @@ -182,7 +182,10 @@ type stubOSSession struct { err error } -func (s *stubOSSession) SaveData(ctx context.Context, name string, data io.Reader, meta map[string]string, timeout time.Duration) (string, error) { +func (s *stubOSSession) OS() drivers.OSDriver { + return nil +} +func (s *stubOSSession) SaveData(ctx context.Context, name string, data io.Reader, meta *drivers.FileProperties, timeout time.Duration) (string, error) { s.saved = append(s.saved, name) return "saved_" + name, s.err } @@ -200,11 +203,17 @@ func (s *stubOSSession) IsOwn(url string) bool { func (s *stubOSSession) ListFiles(ctx context.Context, prefix, delim string) (drivers.PageInfo, error) { return nil, nil } +func (os *stubOSSession) DeleteFile(ctx context.Context, name string) error { + return nil +} func (s *stubOSSession) ReadData(ctx context.Context, name string) (*drivers.FileInfoReader, error) { return nil, nil } -func (s *stubOSSession) OS() drivers.OSDriver { - return nil +func (os *stubOSSession) ReadDataRange(ctx context.Context, name, byteRange string) (*drivers.FileInfoReader, error) { + return nil, nil +} +func (os *stubOSSession) Presign(name string, expire time.Duration) (string, error) { + return "", nil } type stubPlaylistManager struct { @@ -385,7 +394,7 @@ func TestSelectSession_MultipleInFlight2(t *testing.T) { defer func() { getOrchestratorInfoRPC = oldGetOrchestratorInfoRPC }() orchInfoCalled := 0 - getOrchestratorInfoRPC = func(ctx context.Context, bcast common.Broadcaster, orchestratorServer *url.URL) (*net.OrchestratorInfo, error) { + getOrchestratorInfoRPC = func(ctx context.Context, bcast common.Broadcaster, orchestratorServer *url.URL, caps *net.Capabilities) (*net.OrchestratorInfo, error) { orchInfoCalled++ return successOrchInfoUpdate, nil } @@ -604,7 +613,7 @@ func TestTranscodeSegment_RefreshSession(t *testing.T) { oldGetOrchestratorInfoRPC := getOrchestratorInfoRPC defer func() { getOrchestratorInfoRPC = oldGetOrchestratorInfoRPC }() - getOrchestratorInfoRPC = func(ctx context.Context, bcast common.Broadcaster, orchestratorServer *url.URL) (*net.OrchestratorInfo, error) { + getOrchestratorInfoRPC = func(ctx context.Context, bcast common.Broadcaster, orchestratorServer *url.URL, caps *net.Capabilities) (*net.OrchestratorInfo, error) { return successOrchInfoUpdate, nil } @@ -1503,7 +1512,7 @@ func TestRefreshSession(t *testing.T) { assert.Contains(err.Error(), "invalid control character in URL") // trigger getOrchestratorInfo error - getOrchestratorInfoRPC = func(ctx context.Context, bcast common.Broadcaster, orchestratorServer *url.URL) (*net.OrchestratorInfo, error) { + getOrchestratorInfoRPC = func(ctx context.Context, bcast common.Broadcaster, orchestratorServer *url.URL, caps *net.Capabilities) (*net.OrchestratorInfo, error) { return nil, errors.New("some error") } sess = StubBroadcastSession("foo") @@ -1511,7 +1520,7 @@ func TestRefreshSession(t *testing.T) { assert.EqualError(err, "some error") // trigger update - getOrchestratorInfoRPC = func(ctx context.Context, bcast common.Broadcaster, orchestratorServer *url.URL) (*net.OrchestratorInfo, error) { + getOrchestratorInfoRPC = func(ctx context.Context, bcast common.Broadcaster, orchestratorServer *url.URL, caps *net.Capabilities) (*net.OrchestratorInfo, error) { return successOrchInfoUpdate, nil } err = refreshSession(context.TODO(), sess) @@ -1522,7 +1531,7 @@ func TestRefreshSession(t *testing.T) { oldRefreshTimeout := refreshTimeout defer func() { refreshTimeout = oldRefreshTimeout }() refreshTimeout = 10 * time.Millisecond - getOrchestratorInfoRPC = func(ctx context.Context, bcast common.Broadcaster, serv *url.URL) (*net.OrchestratorInfo, error) { + getOrchestratorInfoRPC = func(ctx context.Context, bcast common.Broadcaster, serv *url.URL, caps *net.Capabilities) (*net.OrchestratorInfo, error) { // Wait until the refreshTimeout has elapsed select { case <-ctx.Done(): From 6292df161f1c719e42eab42af152b0b748b32a1a Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Mon, 29 Jul 2024 16:29:40 +0200 Subject: [PATCH 144/203] feat(ai): add dynamic pricePerUnit feature to AI pricing This commit ensures that Orchestrators can set their pricing in USD and the price gets updated dynamically. --- cmd/livepeer/starter/starter.go | 321 +++---- core/ai.go | 33 +- core/capabilities.go | 4 +- core/capabilities_test.go | 21 +- core/livepeernode.go | 16 +- core/livepeernode_test.go | 26 + ..._RoundTrip_Net-20240729130524-3824236.fail | 828 ++++++++++++++++++ go.mod | 2 + 8 files changed, 1084 insertions(+), 167 deletions(-) create mode 100644 core/testdata/rapid/TestCapability_RoundTrip_Net/TestCapability_RoundTrip_Net-20240729130524-3824236.fail diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index 29c08d246..6f9a11e0e 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -515,152 +515,6 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { } } - var aiCaps []core.Capability - capabilityConstraints := make(map[core.Capability]*core.PerCapabilityConstraints) - - if *cfg.AIWorker { - gpus := []string{} - if *cfg.Nvidia != "" { - var err error - gpus, err = common.ParseAccelDevices(*cfg.Nvidia, ffmpeg.Nvidia) - if err != nil { - glog.Errorf("Error parsing -nvidia for devices: %v", err) - return - } - } - - modelsDir := *cfg.AIModelsDir - if modelsDir == "" { - var err error - modelsDir, err = filepath.Abs(path.Join(*cfg.Datadir, "models")) - if err != nil { - glog.Error("Error creating absolute path for models dir: %v", modelsDir) - return - } - } - - if err := os.MkdirAll(modelsDir, 0755); err != nil { - glog.Error("Error creating models dir %v", modelsDir) - return - } - - n.AIWorker, err = worker.NewWorker(*cfg.AIRunnerImage, gpus, modelsDir) - if err != nil { - glog.Errorf("Error starting AI worker: %v", err) - return - } - - if *cfg.AIModels != "" { - configs, err := core.ParseAIModelConfigs(*cfg.AIModels) - if err != nil { - glog.Errorf("Error parsing -aiModels: %v", err) - return - } - - for _, config := range configs { - modelConstraint := &core.ModelConstraint{Warm: config.Warm} - - // If the config contains a URL we call Warm() anyway because AIWorker will just register - // the endpoint for an external container - if config.Warm || config.URL != "" { - endpoint := worker.RunnerEndpoint{URL: config.URL, Token: config.Token} - if err := n.AIWorker.Warm(ctx, config.Pipeline, config.ModelID, endpoint, config.OptimizationFlags); err != nil { - glog.Errorf("Error AI worker warming %v container: %v", config.Pipeline, err) - return - } - } - - // Show warning if people set OptimizationFlags but not Warm. - if len(config.OptimizationFlags) > 0 && !config.Warm { - glog.Warningf("Model %v has 'optimization_flags' set without 'warm'. Optimization flags are currently only used for warm containers.", config.ModelID) - } - - switch config.Pipeline { - case "text-to-image": - _, ok := capabilityConstraints[core.Capability_TextToImage] - if !ok { - aiCaps = append(aiCaps, core.Capability_TextToImage) - capabilityConstraints[core.Capability_TextToImage] = &core.PerCapabilityConstraints{ - Models: make(map[string]*core.ModelConstraint), - } - } - - capabilityConstraints[core.Capability_TextToImage].Models[config.ModelID] = modelConstraint - - n.SetBasePriceForCap("default", core.Capability_TextToImage, config.ModelID, big.NewRat(config.PricePerUnit, config.PixelsPerUnit)) - case "image-to-image": - _, ok := capabilityConstraints[core.Capability_ImageToImage] - if !ok { - aiCaps = append(aiCaps, core.Capability_ImageToImage) - capabilityConstraints[core.Capability_ImageToImage] = &core.PerCapabilityConstraints{ - Models: make(map[string]*core.ModelConstraint), - } - } - - capabilityConstraints[core.Capability_ImageToImage].Models[config.ModelID] = modelConstraint - - n.SetBasePriceForCap("default", core.Capability_ImageToImage, config.ModelID, big.NewRat(config.PricePerUnit, config.PixelsPerUnit)) - case "image-to-video": - _, ok := capabilityConstraints[core.Capability_ImageToVideo] - if !ok { - aiCaps = append(aiCaps, core.Capability_ImageToVideo) - capabilityConstraints[core.Capability_ImageToVideo] = &core.PerCapabilityConstraints{ - Models: make(map[string]*core.ModelConstraint), - } - } - - capabilityConstraints[core.Capability_ImageToVideo].Models[config.ModelID] = modelConstraint - - n.SetBasePriceForCap("default", core.Capability_ImageToVideo, config.ModelID, big.NewRat(config.PricePerUnit, config.PixelsPerUnit)) - case "upscale": - _, ok := capabilityConstraints[core.Capability_Upscale] - if !ok { - aiCaps = append(aiCaps, core.Capability_Upscale) - capabilityConstraints[core.Capability_Upscale] = &core.PerCapabilityConstraints{ - Models: make(map[string]*core.ModelConstraint), - } - } - - capabilityConstraints[core.Capability_Upscale].Models[config.ModelID] = modelConstraint - - n.SetBasePriceForCap("default", core.Capability_Upscale, config.ModelID, big.NewRat(config.PricePerUnit, config.PixelsPerUnit)) - case "audio-to-text": - _, ok := capabilityConstraints[core.Capability_AudioToText] - if !ok { - aiCaps = append(aiCaps, core.Capability_AudioToText) - capabilityConstraints[core.Capability_AudioToText] = &core.PerCapabilityConstraints{ - Models: make(map[string]*core.ModelConstraint), - } - } - - capabilityConstraints[core.Capability_AudioToText].Models[config.ModelID] = modelConstraint - - n.SetBasePriceForCap("default", core.Capability_AudioToText, config.ModelID, big.NewRat(config.PricePerUnit, config.PixelsPerUnit)) - } - - if len(aiCaps) > 0 { - capability := aiCaps[len(aiCaps)-1] - price := n.GetBasePriceForCap("default", capability, config.ModelID) - glog.V(6).Infof("Capability %s (ID: %v) advertised with model constraint %s at price %d per %d unit", config.Pipeline, capability, config.ModelID, price.Num(), price.Denom()) - } - } - } else { - glog.Error("The '-aiModels' flag was set, but no model configuration was provided. Please specify the model configuration using the '-aiModels' flag.") - return - } - - defer func() { - ctx, cancel := context.WithTimeout(context.Background(), aiWorkerContainerStopTimeout) - defer cancel() - if err := n.AIWorker.Stop(ctx); err != nil { - glog.Errorf("Error stopping AI worker containers: %v", err) - return - } - - glog.Infof("Stopped AI worker containers") - }() - } - if *cfg.Redeemer { n.NodeType = core.RedeemerNode } else if *cfg.Orchestrator { @@ -718,7 +572,6 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { glog.Error(err) return } - } else { n.SelectionAlgorithm, err = createSelectionAlgorithm(cfg) if err != nil { @@ -946,7 +799,11 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { } pricePerPixel := new(big.Rat).Quo(pricePerUnit, pixelsPerUnit) autoPrice, err := core.NewAutoConvertedPrice(currency, pricePerPixel, func(price *big.Rat) { - glog.Infof("Price: %v wei per pixel\n ", price.FloatString(3)) + unit := "pixel" + if *cfg.AIWorker { + unit = "compute unit" + } + glog.Infof("Price: %v wei per %s\n", price.FloatString(3), unit) }) if err != nil { panic(fmt.Errorf("Error converting price: %v", err)) @@ -1201,6 +1058,174 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { }() } + var aiCaps []core.Capability + capabilityConstraints := make(map[core.Capability]*core.PerCapabilityConstraints) + + if *cfg.AIWorker { + gpus := []string{} + if *cfg.Nvidia != "" { + var err error + gpus, err = common.ParseAccelDevices(*cfg.Nvidia, ffmpeg.Nvidia) + if err != nil { + glog.Errorf("Error parsing -nvidia for devices: %v", err) + return + } + } + + modelsDir := *cfg.AIModelsDir + if modelsDir == "" { + var err error + modelsDir, err = filepath.Abs(path.Join(*cfg.Datadir, "models")) + if err != nil { + glog.Error("Error creating absolute path for models dir: %v", modelsDir) + return + } + } + + if err := os.MkdirAll(modelsDir, 0755); err != nil { + glog.Error("Error creating models dir %v", modelsDir) + return + } + + n.AIWorker, err = worker.NewWorker(*cfg.AIRunnerImage, gpus, modelsDir) + if err != nil { + glog.Errorf("Error starting AI worker: %v", err) + return + } + + if *cfg.AIModels != "" { + configs, err := core.ParseAIModelConfigs(*cfg.AIModels) + if err != nil { + glog.Errorf("Error parsing -aiModels: %v", err) + return + } + + for _, config := range configs { + modelConstraint := &core.ModelConstraint{Warm: config.Warm} + + // Set price per unit base info. + pixelsPerUnit, ok := new(big.Rat).SetString(*cfg.PixelsPerUnit) + if !ok || !pixelsPerUnit.IsInt() { + panic(fmt.Errorf("-pixelsPerUnit must be a valid integer, provided %v", *cfg.PixelsPerUnit)) + } + if pixelsPerUnit.Sign() <= 0 { + // Can't divide by 0 + panic(fmt.Errorf("-pixelsPerUnit must be > 0, provided %v", *cfg.PixelsPerUnit)) + } + pricePerUnit, currency, err := parsePricePerUnit(config.PricePerUnit.String()) + if err != nil { + panic(fmt.Errorf("'pricePerUnit' value specified for model '%v' in pipeline '%v' must be a valid integer with an optional currency, provided %v", config.ModelID, config.Pipeline, config.PricePerUnit)) + } else if pricePerUnit.Sign() < 0 { + panic(fmt.Errorf("'pricePerUnit' value specified for model '%v' in pipeline '%v' must be >= 0, provided %v", config.ModelID, config.Pipeline, config.PricePerUnit)) + } + pricePerPixel := new(big.Rat).Quo(pricePerUnit, pixelsPerUnit) + autoPrice, err := core.NewAutoConvertedPrice(currency, pricePerPixel, nil) + if err != nil { + panic(fmt.Errorf("error converting price: %v", err)) + } + + // If the config contains a URL we call Warm() anyway because AIWorker will just register + // the endpoint for an external container + if config.Warm || config.URL != "" { + endpoint := worker.RunnerEndpoint{URL: config.URL, Token: config.Token} + if err := n.AIWorker.Warm(ctx, config.Pipeline, config.ModelID, endpoint, config.OptimizationFlags); err != nil { + glog.Errorf("Error AI worker warming %v container: %v", config.Pipeline, err) + return + } + } + + // Show warning if people set OptimizationFlags but not Warm. + if len(config.OptimizationFlags) > 0 && !config.Warm { + glog.Warningf("Model %v has 'optimization_flags' set without 'warm'. Optimization flags are currently only used for warm containers.", config.ModelID) + } + + switch config.Pipeline { + case "text-to-image": + _, ok := capabilityConstraints[core.Capability_TextToImage] + if !ok { + aiCaps = append(aiCaps, core.Capability_TextToImage) + capabilityConstraints[core.Capability_TextToImage] = &core.PerCapabilityConstraints{ + Models: make(map[string]*core.ModelConstraint), + } + } + + capabilityConstraints[core.Capability_TextToImage].Models[config.ModelID] = modelConstraint + + n.SetBasePriceForCap("default", core.Capability_TextToImage, config.ModelID, autoPrice) + case "image-to-image": + _, ok := capabilityConstraints[core.Capability_ImageToImage] + if !ok { + aiCaps = append(aiCaps, core.Capability_ImageToImage) + capabilityConstraints[core.Capability_ImageToImage] = &core.PerCapabilityConstraints{ + Models: make(map[string]*core.ModelConstraint), + } + } + + capabilityConstraints[core.Capability_ImageToImage].Models[config.ModelID] = modelConstraint + + n.SetBasePriceForCap("default", core.Capability_ImageToImage, config.ModelID, autoPrice) + case "image-to-video": + _, ok := capabilityConstraints[core.Capability_ImageToVideo] + if !ok { + aiCaps = append(aiCaps, core.Capability_ImageToVideo) + capabilityConstraints[core.Capability_ImageToVideo] = &core.PerCapabilityConstraints{ + Models: make(map[string]*core.ModelConstraint), + } + } + + capabilityConstraints[core.Capability_ImageToVideo].Models[config.ModelID] = modelConstraint + + n.SetBasePriceForCap("default", core.Capability_ImageToVideo, config.ModelID, autoPrice) + case "upscale": + _, ok := capabilityConstraints[core.Capability_Upscale] + if !ok { + aiCaps = append(aiCaps, core.Capability_Upscale) + capabilityConstraints[core.Capability_Upscale] = &core.PerCapabilityConstraints{ + Models: make(map[string]*core.ModelConstraint), + } + } + + capabilityConstraints[core.Capability_Upscale].Models[config.ModelID] = modelConstraint + + n.SetBasePriceForCap("default", core.Capability_Upscale, config.ModelID, autoPrice) + case "audio-to-text": + _, ok := capabilityConstraints[core.Capability_AudioToText] + if !ok { + aiCaps = append(aiCaps, core.Capability_AudioToText) + capabilityConstraints[core.Capability_AudioToText] = &core.PerCapabilityConstraints{ + Models: make(map[string]*core.ModelConstraint), + } + } + + capabilityConstraints[core.Capability_AudioToText].Models[config.ModelID] = modelConstraint + + n.SetBasePriceForCap("default", core.Capability_AudioToText, config.ModelID, autoPrice) + } + + if len(aiCaps) > 0 { + capability := aiCaps[len(aiCaps)-1] + price := n.GetBasePriceForCap("default", capability, config.ModelID) + pricePerUnit := price.Num().Int64() / price.Denom().Int64() + glog.V(6).Infof("Capability %s (ID: %v) advertised with model constraint %s at price %d wei per compute unit", config.Pipeline, capability, config.ModelID, pricePerUnit) + } + } + } else { + glog.Error("The '-aiModels' flag was set, but no model configuration was provided. Please specify the model configuration using the '-aiModels' flag.") + return + } + + defer func() { + ctx, cancel := context.WithTimeout(context.Background(), aiWorkerContainerStopTimeout) + defer cancel() + if err := n.AIWorker.Stop(ctx); err != nil { + glog.Errorf("Error stopping AI worker containers: %v", err) + return + } + + glog.Infof("Stopped AI worker containers") + }() + } + if *cfg.Objectstore != "" { prepared, err := drivers.PrepareOSURL(*cfg.Objectstore) if err != nil { diff --git a/core/ai.go b/core/ai.go index 772712e97..93966bd41 100644 --- a/core/ai.go +++ b/core/ai.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "errors" + "fmt" "os" "regexp" "strconv" @@ -23,14 +24,40 @@ type AI interface { HasCapacity(pipeline, modelID string) bool } +// Custom type to handle both string and int but store as string. +type StringInt string + +// UnmarshalJSON method to handle both string and int. +func (s *StringInt) UnmarshalJSON(data []byte) error { + // Try to unmarshal as int. + var intValue int64 + if err := json.Unmarshal(data, &intValue); err == nil { + *s = StringInt(strconv.FormatInt(intValue, 10)) + return nil + } + + var strValue string + if err := json.Unmarshal(data, &strValue); err == nil { + *s = StringInt(strValue) + return nil + } + + return fmt.Errorf("invalid value for StringInt: %s", data) +} + +// String converts the StringInt type to a string. +func (s StringInt) String() string { + return string(s) +} + type AIModelConfig struct { Pipeline string `json:"pipeline"` ModelID string `json:"model_id"` URL string `json:"url,omitempty"` Token string `json:"token,omitempty"` Warm bool `json:"warm,omitempty"` - PricePerUnit int64 `json:"price_per_unit,omitempty"` - PixelsPerUnit int64 `json:"pixels_per_unit,omitempty"` + PricePerUnit StringInt `json:"price_per_unit,omitempty"` + PixelsPerUnit StringInt `json:"pixels_per_unit,omitempty"` OptimizationFlags worker.OptimizationFlags `json:"optimization_flags,omitempty"` } @@ -39,7 +66,7 @@ func (config *AIModelConfig) UnmarshalJSON(data []byte) error { type AIModelConfigAlias AIModelConfig // Set default values for fields defaultConfig := &AIModelConfigAlias{ - PixelsPerUnit: 1, + PixelsPerUnit: "1", } if err := json.Unmarshal(data, defaultConfig); err != nil { diff --git a/core/capabilities.go b/core/capabilities.go index cfeb84046..4f4122ec7 100644 --- a/core/capabilities.go +++ b/core/capabilities.go @@ -479,7 +479,7 @@ func CapabilitiesFromNetCapabilities(caps *net.Capabilities) *Capabilities { capacities: make(map[Capability]int), version: caps.Version, constraints: Constraints{minVersion: caps.Constraints.GetMinVersion()}, - capabilityConstraints: make(map[Capability]*PerCapabilityConstraints), + capabilityConstraints: make(CapabilityConstraints), } if caps.Capacities == nil || len(caps.Capacities) == 0 { // build capacities map if not present (struct received from previous versions) @@ -512,7 +512,7 @@ func CapabilitiesFromNetCapabilities(caps *net.Capabilities) *Capabilities { } func NewCapabilities(caps []Capability, m []Capability) *Capabilities { - c := &Capabilities{capacities: make(map[Capability]int), version: LivepeerVersion} + c := &Capabilities{capacities: make(map[Capability]int), version: LivepeerVersion, capabilityConstraints: make(CapabilityConstraints)} if len(caps) > 0 { c.bitstring = NewCapabilityString(caps) // initialize capacities to 1 by default, mandatory capabilities doesn't have capacities diff --git a/core/capabilities_test.go b/core/capabilities_test.go index b165a3d9c..f4a905552 100644 --- a/core/capabilities_test.go +++ b/core/capabilities_test.go @@ -396,26 +396,35 @@ type stubOS struct { storageType int32 } +func (os *stubOS) OS() drivers.OSDriver { + return nil +} +func (os *stubOS) SaveData(context.Context, string, io.Reader, *drivers.FileProperties, time.Duration) (string, error) { + return "", nil +} +func (os *stubOS) EndSession() {} func (os *stubOS) GetInfo() *drivers.OSInfo { if os.storageType == stubOSMagic { return nil } return &drivers.OSInfo{StorageType: drivers.OSInfo_StorageType(os.storageType)} } -func (os *stubOS) EndSession() {} -func (os *stubOS) SaveData(context.Context, string, io.Reader, map[string]string, time.Duration) (string, error) { - return "", nil -} func (os *stubOS) IsExternal() bool { return false } func (os *stubOS) IsOwn(url string) bool { return true } func (os *stubOS) ListFiles(ctx context.Context, prefix, delim string) (drivers.PageInfo, error) { return nil, nil } +func (os *stubOS) DeleteFile(ctx context.Context, name string) error { + return nil +} func (os *stubOS) ReadData(ctx context.Context, name string) (*drivers.FileInfoReader, error) { return nil, nil } -func (os *stubOS) OS() drivers.OSDriver { - return nil +func (os *stubOS) ReadDataRange(ctx context.Context, name, byteRange string) (*drivers.FileInfoReader, error) { + return nil, nil +} +func (os *stubOS) Presign(name string, expire time.Duration) (string, error) { + return "", nil } func TestCapability_StorageToCapability(t *testing.T) { diff --git a/core/livepeernode.go b/core/livepeernode.go index e33c435f6..0d726ce65 100644 --- a/core/livepeernode.go +++ b/core/livepeernode.go @@ -64,20 +64,20 @@ func (t NodeType) String() string { } type CapabilityPriceMenu struct { - modelPrices map[string]*big.Rat + modelPrices map[string]*AutoConvertedPrice } func NewCapabilityPriceMenu() CapabilityPriceMenu { return CapabilityPriceMenu{ - modelPrices: make(map[string]*big.Rat), + modelPrices: make(map[string]*AutoConvertedPrice), } } -func (m CapabilityPriceMenu) SetPriceForModelID(modelID string, price *big.Rat) { +func (m CapabilityPriceMenu) SetPriceForModelID(modelID string, price *AutoConvertedPrice) { m.modelPrices[modelID] = price } -func (m CapabilityPriceMenu) PriceForModelID(modelID string) *big.Rat { +func (m CapabilityPriceMenu) PriceForModelID(modelID string) *AutoConvertedPrice { return m.modelPrices[modelID] } @@ -87,7 +87,7 @@ func NewCapabilityPrices() CapabilityPrices { return make(map[Capability]CapabilityPriceMenu) } -func (cp CapabilityPrices) SetPriceForModelID(cap Capability, modelID string, price *big.Rat) { +func (cp CapabilityPrices) SetPriceForModelID(cap Capability, modelID string, price *AutoConvertedPrice) { menu, ok := cp[cap] if !ok { menu = NewCapabilityPriceMenu() @@ -97,7 +97,7 @@ func (cp CapabilityPrices) SetPriceForModelID(cap Capability, modelID string, pr menu.SetPriceForModelID(modelID, price) } -func (cp CapabilityPrices) PriceForModelID(cap Capability, modelID string) *big.Rat { +func (cp CapabilityPrices) PriceForModelID(cap Capability, modelID string) *AutoConvertedPrice { menu, ok := cp[cap] if !ok { return nil @@ -212,7 +212,7 @@ func (n *LivepeerNode) GetBasePrices() map[string]*big.Rat { return prices } -func (n *LivepeerNode) SetBasePriceForCap(b_eth_addr string, cap Capability, modelID string, price *big.Rat) { +func (n *LivepeerNode) SetBasePriceForCap(b_eth_addr string, cap Capability, modelID string, price *AutoConvertedPrice) { addr := strings.ToLower(b_eth_addr) n.mu.Lock() defer n.mu.Unlock() @@ -236,7 +236,7 @@ func (n *LivepeerNode) GetBasePriceForCap(b_eth_addr string, cap Capability, mod return nil } - return prices.PriceForModelID(cap, modelID) + return prices.PriceForModelID(cap, modelID).Value() } // SetMaxFaceValue sets the faceValue upper limit for tickets received diff --git a/core/livepeernode_test.go b/core/livepeernode_test.go index 230f8dd42..d943086ba 100644 --- a/core/livepeernode_test.go +++ b/core/livepeernode_test.go @@ -179,3 +179,29 @@ func TestSetAndGetBasePrice(t *testing.T) { assert.Zero(n.GetBasePrices()[addr1].Cmp(price1)) assert.Zero(n.GetBasePrices()[addr2].Cmp(price2)) } + +func TestSetAndGetCapabilityPrices(t *testing.T) { + require := require.New(t) + assert := assert.New(t) + + n, err := NewLivepeerNode(nil, "", nil) + require.Nil(err) + + price := big.NewRat(1, 1) + + n.SetBasePriceForCap("default", Capability_TextToImage, "default", NewFixedPrice(price)) + assert.Zero(n.priceInfoForCaps["default"].PriceForModelID(Capability_TextToImage, "default").Value().Cmp(price)) + assert.Zero(n.GetBasePriceForCap("default", Capability_TextToImage, "default").Cmp(price)) + + addr1 := "0x0000000000000000000000000000000000000000" + addr2 := "0x1000000000000000000000000000000000000000" + price1 := big.NewRat(2, 1) + price2 := big.NewRat(3, 1) + + n.SetBasePriceForCap(addr1, Capability_TextToImage, "default", NewFixedPrice(price1)) + n.SetBasePriceForCap(addr2, Capability_ImageToImage, "default", NewFixedPrice(price2)) + assert.Zero(n.priceInfoForCaps[addr1].PriceForModelID(Capability_TextToImage, "default").Value().Cmp(price1)) + assert.Zero(n.priceInfoForCaps[addr2].PriceForModelID(Capability_ImageToImage, "default").Value().Cmp(price2)) + assert.Zero(n.GetBasePriceForCap(addr1, Capability_TextToImage, "default").Cmp(price1)) + assert.Zero(n.GetBasePriceForCap(addr2, Capability_ImageToImage, "default").Cmp(price2)) +} diff --git a/core/testdata/rapid/TestCapability_RoundTrip_Net/TestCapability_RoundTrip_Net-20240729130524-3824236.fail b/core/testdata/rapid/TestCapability_RoundTrip_Net/TestCapability_RoundTrip_Net-20240729130524-3824236.fail new file mode 100644 index 000000000..bfa48715a --- /dev/null +++ b/core/testdata/rapid/TestCapability_RoundTrip_Net/TestCapability_RoundTrip_Net-20240729130524-3824236.fail @@ -0,0 +1,828 @@ +# 2024/07/29 13:05:24.695037 [TestCapability_RoundTrip_Net] [rapid] draw capLen: 54 +# 2024/07/29 13:05:24.695039 [TestCapability_RoundTrip_Net] [rapid] draw cap: 464 +# 2024/07/29 13:05:24.695040 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695040 [TestCapability_RoundTrip_Net] [rapid] draw cap: 461 +# 2024/07/29 13:05:24.695041 [TestCapability_RoundTrip_Net] [rapid] draw cap: 509 +# 2024/07/29 13:05:24.695041 [TestCapability_RoundTrip_Net] [rapid] draw cap: 429 +# 2024/07/29 13:05:24.695042 [TestCapability_RoundTrip_Net] [rapid] draw cap: 39 +# 2024/07/29 13:05:24.695042 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695043 [TestCapability_RoundTrip_Net] [rapid] draw cap: 9 +# 2024/07/29 13:05:24.695043 [TestCapability_RoundTrip_Net] [rapid] draw cap: 291 +# 2024/07/29 13:05:24.695044 [TestCapability_RoundTrip_Net] [rapid] draw cap: 4 +# 2024/07/29 13:05:24.695045 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695045 [TestCapability_RoundTrip_Net] [rapid] draw cap: 27 +# 2024/07/29 13:05:24.695045 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 +# 2024/07/29 13:05:24.695046 [TestCapability_RoundTrip_Net] [rapid] draw cap: 214 +# 2024/07/29 13:05:24.695046 [TestCapability_RoundTrip_Net] [rapid] draw cap: 192 +# 2024/07/29 13:05:24.695047 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695047 [TestCapability_RoundTrip_Net] [rapid] draw cap: 484 +# 2024/07/29 13:05:24.695047 [TestCapability_RoundTrip_Net] [rapid] draw cap: 165 +# 2024/07/29 13:05:24.695048 [TestCapability_RoundTrip_Net] [rapid] draw cap: 177 +# 2024/07/29 13:05:24.695049 [TestCapability_RoundTrip_Net] [rapid] draw cap: 213 +# 2024/07/29 13:05:24.695049 [TestCapability_RoundTrip_Net] [rapid] draw cap: 18 +# 2024/07/29 13:05:24.695050 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 +# 2024/07/29 13:05:24.695050 [TestCapability_RoundTrip_Net] [rapid] draw cap: 9 +# 2024/07/29 13:05:24.695050 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 +# 2024/07/29 13:05:24.695051 [TestCapability_RoundTrip_Net] [rapid] draw cap: 12 +# 2024/07/29 13:05:24.695051 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695051 [TestCapability_RoundTrip_Net] [rapid] draw cap: 437 +# 2024/07/29 13:05:24.695052 [TestCapability_RoundTrip_Net] [rapid] draw cap: 11 +# 2024/07/29 13:05:24.695052 [TestCapability_RoundTrip_Net] [rapid] draw cap: 39 +# 2024/07/29 13:05:24.695052 [TestCapability_RoundTrip_Net] [rapid] draw cap: 30 +# 2024/07/29 13:05:24.695053 [TestCapability_RoundTrip_Net] [rapid] draw cap: 12 +# 2024/07/29 13:05:24.695053 [TestCapability_RoundTrip_Net] [rapid] draw cap: 325 +# 2024/07/29 13:05:24.695053 [TestCapability_RoundTrip_Net] [rapid] draw cap: 204 +# 2024/07/29 13:05:24.695054 [TestCapability_RoundTrip_Net] [rapid] draw cap: 13 +# 2024/07/29 13:05:24.695054 [TestCapability_RoundTrip_Net] [rapid] draw cap: 403 +# 2024/07/29 13:05:24.695055 [TestCapability_RoundTrip_Net] [rapid] draw cap: 498 +# 2024/07/29 13:05:24.695055 [TestCapability_RoundTrip_Net] [rapid] draw cap: 512 +# 2024/07/29 13:05:24.695056 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 +# 2024/07/29 13:05:24.695057 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695058 [TestCapability_RoundTrip_Net] [rapid] draw cap: 136 +# 2024/07/29 13:05:24.695058 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 +# 2024/07/29 13:05:24.695058 [TestCapability_RoundTrip_Net] [rapid] draw cap: 169 +# 2024/07/29 13:05:24.695059 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695059 [TestCapability_RoundTrip_Net] [rapid] draw cap: 62 +# 2024/07/29 13:05:24.695059 [TestCapability_RoundTrip_Net] [rapid] draw cap: 181 +# 2024/07/29 13:05:24.695060 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695060 [TestCapability_RoundTrip_Net] [rapid] draw cap: 6 +# 2024/07/29 13:05:24.695061 [TestCapability_RoundTrip_Net] [rapid] draw cap: 12 +# 2024/07/29 13:05:24.695061 [TestCapability_RoundTrip_Net] [rapid] draw cap: 512 +# 2024/07/29 13:05:24.695062 [TestCapability_RoundTrip_Net] [rapid] draw cap: 177 +# 2024/07/29 13:05:24.695062 [TestCapability_RoundTrip_Net] [rapid] draw cap: 7 +# 2024/07/29 13:05:24.695063 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695064 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695064 [TestCapability_RoundTrip_Net] [rapid] draw cap: 400 +# 2024/07/29 13:05:24.695065 [TestCapability_RoundTrip_Net] [rapid] draw capLen: 42 +# 2024/07/29 13:05:24.695066 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695067 [TestCapability_RoundTrip_Net] [rapid] draw cap: 6 +# 2024/07/29 13:05:24.695068 [TestCapability_RoundTrip_Net] [rapid] draw cap: 368 +# 2024/07/29 13:05:24.695069 [TestCapability_RoundTrip_Net] [rapid] draw cap: 512 +# 2024/07/29 13:05:24.695069 [TestCapability_RoundTrip_Net] [rapid] draw cap: 10 +# 2024/07/29 13:05:24.695070 [TestCapability_RoundTrip_Net] [rapid] draw cap: 235 +# 2024/07/29 13:05:24.695071 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695071 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695071 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 +# 2024/07/29 13:05:24.695072 [TestCapability_RoundTrip_Net] [rapid] draw cap: 8 +# 2024/07/29 13:05:24.695072 [TestCapability_RoundTrip_Net] [rapid] draw cap: 263 +# 2024/07/29 13:05:24.695072 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695073 [TestCapability_RoundTrip_Net] [rapid] draw cap: 107 +# 2024/07/29 13:05:24.695073 [TestCapability_RoundTrip_Net] [rapid] draw cap: 301 +# 2024/07/29 13:05:24.695074 [TestCapability_RoundTrip_Net] [rapid] draw cap: 13 +# 2024/07/29 13:05:24.695074 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695074 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695075 [TestCapability_RoundTrip_Net] [rapid] draw cap: 11 +# 2024/07/29 13:05:24.695075 [TestCapability_RoundTrip_Net] [rapid] draw cap: 289 +# 2024/07/29 13:05:24.695076 [TestCapability_RoundTrip_Net] [rapid] draw cap: 19 +# 2024/07/29 13:05:24.695076 [TestCapability_RoundTrip_Net] [rapid] draw cap: 68 +# 2024/07/29 13:05:24.695077 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695079 [TestCapability_RoundTrip_Net] [rapid] draw cap: 102 +# 2024/07/29 13:05:24.695079 [TestCapability_RoundTrip_Net] [rapid] draw cap: 10 +# 2024/07/29 13:05:24.695079 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695080 [TestCapability_RoundTrip_Net] [rapid] draw cap: 6 +# 2024/07/29 13:05:24.695080 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695080 [TestCapability_RoundTrip_Net] [rapid] draw cap: 14 +# 2024/07/29 13:05:24.695081 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 +# 2024/07/29 13:05:24.695081 [TestCapability_RoundTrip_Net] [rapid] draw cap: 15 +# 2024/07/29 13:05:24.695081 [TestCapability_RoundTrip_Net] [rapid] draw cap: 54 +# 2024/07/29 13:05:24.695082 [TestCapability_RoundTrip_Net] [rapid] draw cap: 7 +# 2024/07/29 13:05:24.695082 [TestCapability_RoundTrip_Net] [rapid] draw cap: 94 +# 2024/07/29 13:05:24.695083 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695084 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695084 [TestCapability_RoundTrip_Net] [rapid] draw cap: 5 +# 2024/07/29 13:05:24.695084 [TestCapability_RoundTrip_Net] [rapid] draw cap: 206 +# 2024/07/29 13:05:24.695085 [TestCapability_RoundTrip_Net] [rapid] draw cap: 512 +# 2024/07/29 13:05:24.695085 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 +# 2024/07/29 13:05:24.695086 [TestCapability_RoundTrip_Net] [rapid] draw cap: 78 +# 2024/07/29 13:05:24.695086 [TestCapability_RoundTrip_Net] [rapid] draw cap: 11 +# 2024/07/29 13:05:24.695086 [TestCapability_RoundTrip_Net] [rapid] draw cap: 457 +# 2024/07/29 13:05:24.695203 [TestCapability_RoundTrip_Net] +# Error Trace: /home/ricks/development/livepeer/ai/go-livepeer/core/capabilities_test.go:371 +# /home/ricks/go/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /home/ricks/go/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /home/ricks/go/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /home/ricks/go/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:118 +# /home/ricks/development/livepeer/ai/go-livepeer/core/capabilities_test.go:356 +# Error: Not equal: +# expected: &core.Capabilities{bitstring:core.CapabilityString{0x4000008048043adf, 0x0, 0x22022000000100, 0x601001, 0x800000000, 0x20, 0x20200000090000, 0x2004001000012000, 0x1}, mandatories:core.CapabilityString{0x4000000008edef, 0x84040004010, 0x0, 0x80000004000, 0x200200000080, 0x1000000000000, 0x0, 0x200, 0x1}, version:"undefined", constraints:core.Constraints{minVersion:""}, capabilityConstraints:core.CapabilityConstraints(nil), capacities:map[core.Capability]int{0:1, 1:1, 2:1, 3:1, 4:1, 6:1, 7:1, 9:1, 11:1, 12:1, 13:1, 18:1, 27:1, 30:1, 39:1, 62:1, 136:1, 165:1, 169:1, 177:1, 181:1, 192:1, 204:1, 213:1, 214:1, 291:1, 325:1, 400:1, 403:1, 429:1, 437:1, 461:1, 464:1, 484:1, 498:1, 509:1, 512:1}, mutex:sync.Mutex{state:0, sema:0x0}} +# actual : &core.Capabilities{bitstring:core.CapabilityString{0x4000008048043adf, 0x0, 0x22022000000100, 0x601001, 0x800000000, 0x20, 0x20200000090000, 0x2004001000012000, 0x1}, mandatories:core.CapabilityString{0x4000000008edef, 0x84040004010, 0x0, 0x80000004000, 0x200200000080, 0x1000000000000, 0x0, 0x200, 0x1}, version:"undefined", constraints:core.Constraints{minVersion:""}, capabilityConstraints:core.CapabilityConstraints{}, capacities:map[core.Capability]int{0:1, 1:1, 2:1, 3:1, 4:1, 6:1, 7:1, 9:1, 11:1, 12:1, 13:1, 18:1, 27:1, 30:1, 39:1, 62:1, 136:1, 165:1, 169:1, 177:1, 181:1, 192:1, 204:1, 213:1, 214:1, 291:1, 325:1, 400:1, 403:1, 429:1, 437:1, 461:1, 464:1, 484:1, 498:1, 509:1, 512:1}, mutex:sync.Mutex{state:0, sema:0x0}} +# +# Diff: +# --- Expected +# +++ Actual +# @@ -27,3 +27,4 @@ +# }, +# - capabilityConstraints: (core.CapabilityConstraints) , +# + capabilityConstraints: (core.CapabilityConstraints) { +# + }, +# capacities: (map[core.Capability]int) (len=37) { +# Test: TestCapability_RoundTrip_Net +# 2024/07/29 13:05:24.695205 [TestCapability_RoundTrip_Net] [rapid] draw capLen: 0 +# 2024/07/29 13:05:24.695206 [TestCapability_RoundTrip_Net] [rapid] draw capLen: 88 +# 2024/07/29 13:05:24.695207 [TestCapability_RoundTrip_Net] [rapid] draw cap: 4 +# 2024/07/29 13:05:24.695207 [TestCapability_RoundTrip_Net] [rapid] draw cap: 8 +# 2024/07/29 13:05:24.695208 [TestCapability_RoundTrip_Net] [rapid] draw cap: 512 +# 2024/07/29 13:05:24.695208 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695208 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 +# 2024/07/29 13:05:24.695209 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 +# 2024/07/29 13:05:24.695210 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695210 [TestCapability_RoundTrip_Net] [rapid] draw cap: 79 +# 2024/07/29 13:05:24.695210 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695211 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695211 [TestCapability_RoundTrip_Net] [rapid] draw cap: 453 +# 2024/07/29 13:05:24.695212 [TestCapability_RoundTrip_Net] [rapid] draw cap: 232 +# 2024/07/29 13:05:24.695212 [TestCapability_RoundTrip_Net] [rapid] draw cap: 8 +# 2024/07/29 13:05:24.695212 [TestCapability_RoundTrip_Net] [rapid] draw cap: 10 +# 2024/07/29 13:05:24.695213 [TestCapability_RoundTrip_Net] [rapid] draw cap: 239 +# 2024/07/29 13:05:24.695214 [TestCapability_RoundTrip_Net] [rapid] draw cap: 20 +# 2024/07/29 13:05:24.695214 [TestCapability_RoundTrip_Net] [rapid] draw cap: 5 +# 2024/07/29 13:05:24.695215 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695216 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695217 [TestCapability_RoundTrip_Net] [rapid] draw cap: 265 +# 2024/07/29 13:05:24.695217 [TestCapability_RoundTrip_Net] [rapid] draw cap: 150 +# 2024/07/29 13:05:24.695218 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 +# 2024/07/29 13:05:24.695219 [TestCapability_RoundTrip_Net] [rapid] draw cap: 234 +# 2024/07/29 13:05:24.695219 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695223 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695223 [TestCapability_RoundTrip_Net] [rapid] draw cap: 62 +# 2024/07/29 13:05:24.695223 [TestCapability_RoundTrip_Net] [rapid] draw cap: 5 +# 2024/07/29 13:05:24.695224 [TestCapability_RoundTrip_Net] [rapid] draw cap: 512 +# 2024/07/29 13:05:24.695224 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695224 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 +# 2024/07/29 13:05:24.695225 [TestCapability_RoundTrip_Net] [rapid] draw cap: 17 +# 2024/07/29 13:05:24.695225 [TestCapability_RoundTrip_Net] [rapid] draw cap: 5 +# 2024/07/29 13:05:24.695226 [TestCapability_RoundTrip_Net] [rapid] draw cap: 75 +# 2024/07/29 13:05:24.695226 [TestCapability_RoundTrip_Net] [rapid] draw cap: 310 +# 2024/07/29 13:05:24.695226 [TestCapability_RoundTrip_Net] [rapid] draw cap: 172 +# 2024/07/29 13:05:24.695227 [TestCapability_RoundTrip_Net] [rapid] draw cap: 24 +# 2024/07/29 13:05:24.695227 [TestCapability_RoundTrip_Net] [rapid] draw cap: 88 +# 2024/07/29 13:05:24.695228 [TestCapability_RoundTrip_Net] [rapid] draw cap: 512 +# 2024/07/29 13:05:24.695228 [TestCapability_RoundTrip_Net] [rapid] draw cap: 446 +# 2024/07/29 13:05:24.695228 [TestCapability_RoundTrip_Net] [rapid] draw cap: 7 +# 2024/07/29 13:05:24.695229 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 +# 2024/07/29 13:05:24.695229 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 +# 2024/07/29 13:05:24.695229 [TestCapability_RoundTrip_Net] [rapid] draw cap: 114 +# 2024/07/29 13:05:24.695230 [TestCapability_RoundTrip_Net] [rapid] draw cap: 6 +# 2024/07/29 13:05:24.695230 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 +# 2024/07/29 13:05:24.695230 [TestCapability_RoundTrip_Net] [rapid] draw cap: 426 +# 2024/07/29 13:05:24.695231 [TestCapability_RoundTrip_Net] [rapid] draw cap: 13 +# 2024/07/29 13:05:24.695232 [TestCapability_RoundTrip_Net] [rapid] draw cap: 13 +# 2024/07/29 13:05:24.695232 [TestCapability_RoundTrip_Net] [rapid] draw cap: 6 +# 2024/07/29 13:05:24.695233 [TestCapability_RoundTrip_Net] [rapid] draw cap: 29 +# 2024/07/29 13:05:24.695233 [TestCapability_RoundTrip_Net] [rapid] draw cap: 130 +# 2024/07/29 13:05:24.695233 [TestCapability_RoundTrip_Net] [rapid] draw cap: 15 +# 2024/07/29 13:05:24.695234 [TestCapability_RoundTrip_Net] [rapid] draw cap: 196 +# 2024/07/29 13:05:24.695234 [TestCapability_RoundTrip_Net] [rapid] draw cap: 478 +# 2024/07/29 13:05:24.695234 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695235 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695235 [TestCapability_RoundTrip_Net] [rapid] draw cap: 10 +# 2024/07/29 13:05:24.695236 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 +# 2024/07/29 13:05:24.695236 [TestCapability_RoundTrip_Net] [rapid] draw cap: 406 +# 2024/07/29 13:05:24.695236 [TestCapability_RoundTrip_Net] [rapid] draw cap: 145 +# 2024/07/29 13:05:24.695237 [TestCapability_RoundTrip_Net] [rapid] draw cap: 241 +# 2024/07/29 13:05:24.695237 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695237 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 +# 2024/07/29 13:05:24.695238 [TestCapability_RoundTrip_Net] [rapid] draw cap: 5 +# 2024/07/29 13:05:24.695238 [TestCapability_RoundTrip_Net] [rapid] draw cap: 229 +# 2024/07/29 13:05:24.695239 [TestCapability_RoundTrip_Net] [rapid] draw cap: 47 +# 2024/07/29 13:05:24.695239 [TestCapability_RoundTrip_Net] [rapid] draw cap: 512 +# 2024/07/29 13:05:24.695240 [TestCapability_RoundTrip_Net] [rapid] draw cap: 5 +# 2024/07/29 13:05:24.695240 [TestCapability_RoundTrip_Net] [rapid] draw cap: 270 +# 2024/07/29 13:05:24.695241 [TestCapability_RoundTrip_Net] [rapid] draw cap: 148 +# 2024/07/29 13:05:24.695241 [TestCapability_RoundTrip_Net] [rapid] draw cap: 292 +# 2024/07/29 13:05:24.695241 [TestCapability_RoundTrip_Net] [rapid] draw cap: 146 +# 2024/07/29 13:05:24.695242 [TestCapability_RoundTrip_Net] [rapid] draw cap: 411 +# 2024/07/29 13:05:24.695242 [TestCapability_RoundTrip_Net] [rapid] draw cap: 49 +# 2024/07/29 13:05:24.695242 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 +# 2024/07/29 13:05:24.695243 [TestCapability_RoundTrip_Net] [rapid] draw cap: 132 +# 2024/07/29 13:05:24.695243 [TestCapability_RoundTrip_Net] [rapid] draw cap: 9 +# 2024/07/29 13:05:24.695243 [TestCapability_RoundTrip_Net] [rapid] draw cap: 193 +# 2024/07/29 13:05:24.695244 [TestCapability_RoundTrip_Net] [rapid] draw cap: 510 +# 2024/07/29 13:05:24.695244 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 +# 2024/07/29 13:05:24.695245 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695245 [TestCapability_RoundTrip_Net] [rapid] draw cap: 22 +# 2024/07/29 13:05:24.695245 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 +# 2024/07/29 13:05:24.695246 [TestCapability_RoundTrip_Net] [rapid] draw cap: 136 +# 2024/07/29 13:05:24.695246 [TestCapability_RoundTrip_Net] [rapid] draw cap: 408 +# 2024/07/29 13:05:24.695246 [TestCapability_RoundTrip_Net] [rapid] draw cap: 11 +# 2024/07/29 13:05:24.695247 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695247 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# +v0.4.8#8355047278167994326 +0x1ffe553afdc0f7 +0xf45b3a3026fb1 +0x36 +0x19e8d3757890a9 +0x1ece3789377bd6 +0x254 +0x205 +0x1d0 +0xc696ee1730946 +0xa5bfc6000ae4 +0x1 +0x1e767e042e2f9d +0x176d810cfc0f2b +0x267 +0x1cd +0x2a57c0b86b7d2 +0x1e916bccdef442 +0x1fd +0xcf6dc68c6ae6f +0x17398c89c02dec +0x1ad +0x131c5a873b987b +0x157a8063db5cc8 +0x27 +0x1987e962a4ed1 +0x12edc22943d81 +0x0 +0x163376461fd54d +0x9ce4ce56c30ba +0x9 +0x1e5486cb3b6174 +0x16c0f7f21eb8cb +0x123 +0xf0a4d4dd1e518 +0x1ac2347bba71a3 +0x4 +0x169e45fe17b9c4 +0x5c8baba4436ce +0x1 +0x1fa586fec7e96b +0x120720f4f21290 +0x1b +0x4fb6259a712e2 +0x37f35a9300112 +0x3 +0x1935b725e9e96 +0x187a601d1528ca +0xd6 +0x7913ebe9dc13a +0x142e0f585b2dac +0xc0 +0x196f40656a41ec +0x3af286f273a5d +0x0 +0x1a2c3ac100cc8e +0x1a36899482617e +0x3a3 +0x1e4 +0x1b6b16ee1d7b0 +0x1cbffa9417a94e +0xa5 +0x6edec3d67143c +0x127c8c567c73c4 +0xb1 +0x188f8cfc3194f8 +0x15a4da498b416b +0xd5 +0x1ac94151d50f1b +0x1cbdfcfdc9aba8 +0x12 +0x4424d7c0cfd27 +0xc2c679bb08e83 +0x3 +0x13a41a68b5e7b5 +0xe824a9c93fb79 +0x9 +0x104eae942c2d3a +0x8401f4f6add60 +0x2 +0x1a85ae65d696b5 +0xd9a97b75d45a0 +0xc +0x1dcdacf4d1f538 +0x5b79db0b9ce2e +0x1 +0x105690b2cc5155 +0x160e817f276ba9 +0x1b5 +0x1aef64c8f37d26 +0xaf0d1bd20e7f0 +0xb +0x124d67659fe95 +0xef30a4d402397 +0x27 +0xea3815f25e942 +0xd3dd513625b5f +0x1e +0xa34a45d0353c0 +0x181b4b8c96b635 +0x2cf +0x390 +0xc +0x1249d6d7171e3e +0x18466e9bb7d281 +0x3c9 +0x358 +0x145 +0x99299469331c0 +0x1225c985e1f9d7 +0xcc +0x8ae4b0b2d445c +0xa0005e7ee8688 +0xd +0x156193b8049849 +0x1beecbb840472f +0x3c1 +0x318 +0x2ab +0x193 +0x14816d4d091040 +0x1e18e4ad3afb90 +0x312 +0x1f2 +0x16d98afc38a5eb +0x1ffb0857cf3f4e +0xffffffffffffffff +0x63bc8e9d1b67c +0x670439aa47cba +0x3 +0x7d862db5053c3 +0x908d39c551ddc +0x0 +0x1e5beb91ed0b7 +0x11cff49a778ef7 +0x88 +0xc149b27cdeaff +0x5ae64d015a7f1 +0x2 +0x3f1601a4584c9 +0x12b7466721c2e3 +0xa9 +0x141e20558b5b71 +0x1b482d7eb0eab +0x1 +0xfe9be41b1a7d1 +0x11fbe041d5e3a2 +0x3e +0xc05c575caa3e2 +0x12fd3f55f92749 +0xb5 +0xfef307a80649c +0x694d773b6e563 +0x0 +0x1284e54af89718 +0x8f1a506967360 +0x6 +0x1eecbbac2653a2 +0xafb39c58bdb30 +0xc +0x1397764b96e3d7 +0x1fcf6c00def779 +0xffffffffffffffff +0xd943ace824438 +0x1234ba12e2b5d9 +0xb1 +0xc079ec12eaad5 +0x7e0bf0514df4a +0x7 +0x151b5694ff1a29 +0x3699052aae4ed +0x0 +0x160ee2a3299343 +0x94725373b3b5e +0x0 +0x19762e14c2a5a5 +0x1d3d3495650207 +0x190 +0x16cfffcbf32568 +0xe62e37e3841db +0x2a +0x11079c4e5b4840 +0x54fc022a5fc28 +0x0 +0x12cb7927ef68c +0xab6a464b9bb44 +0x6 +0x1396451ba612f3 +0x15955083a4f426 +0x2c4 +0x3c2 +0x170 +0x8ad02f4160198 +0x1f2e292b02fc83 +0xffffffffffffffff +0x947f2842e50d6 +0xeaa1a5622e3a4 +0xa +0x1a1704c8278973 +0x12a05ed66f4c0a +0xeb +0x1114fdc6207cd2 +0x3bb4cfcac042b +0x1 +0x1b3b74b3d01456 +0x531f320b2531b +0x1 +0x197e4b75069d1c +0x4d8176640b92e +0x3 +0x1b012144f0fa51 +0x10fae59448f46a +0x8 +0x10539663fa4035 +0x164c0b334cded1 +0x37c +0x107 +0x125a9a06e7bba5 +0xb9b43c76717b +0x1 +0x1cf2aeb6603cb3 +0x149038ed5d9d11 +0x6b +0xf173ebe9135e5 +0x1811a49e35de09 +0x12d +0xb813b577702a +0xafe2049d3f562 +0xd +0x1ba2b7b064c255 +0x20607cec5b805 +0x1 +0x16c2154199ef32 +0x23f3c71d80c9b +0x1 +0x1106d63649cf19 +0xa2b99bca8e7ac +0xb +0xa6d34ffecd0a5 +0x159f02878e5f6d +0x2ea +0x2ba +0x31b +0x121 +0x17762d304d229e +0x1ae07bc79e4410 +0x13 +0x16520166b2318f +0x18b9eed5884993 +0x39e +0x44 +0x156a3d2e785604 +0x6de195e62fd9c +0x1 +0x357a4cb6e9746 +0x1ec3aa7ce66580 +0x2cd +0x66 +0x17ac30ce8ec31b +0x9497852492b84 +0xa +0x18ac792a689485 +0x3266d37f6731d +0x0 +0x158865b1697fc3 +0xb5ea2b426535e +0x6 +0x1db03d2d540db2 +0x48084744eebcc +0x1 +0xf03b281d81e80 +0x10b4af861d654f +0xe +0x1b54ecc34b53e2 +0x6106d8d99c88d +0x3 +0xb165bcd6cecde +0xf3ff59d242ab5 +0xf +0x340e867d1532d +0x1130568709d9e7 +0x36 +0x12d1ffb3754d5b +0xa84458eda2d0e +0x7 +0x1a64f2673adbc7 +0x18822fb55da60d +0x30c +0x230 +0x393 +0x5e +0x1249caf2fa4cc7 +0x1090841a6a3c7 +0x1 +0xbbab3008bcd4b +0x5e0cf2416db3c +0x1 +0x1ae89840ef2766 +0x8c200ba4313b8 +0x5 +0x1fee6a270f448d +0x11d30f6fb09a46 +0xce +0xe8381b8eda998 +0x1f1e4a7c6aa3ec +0xffffffffffffffff +0xf6462d88f7320 +0x49d09a27bb9f3 +0x2 +0x416e964435cca +0x1e1ff6f6b79794 +0x367 +0x2a1 +0x4e +0x2c8f3bf759265 +0xc76dc2ff2479b +0xb +0x1b474ddd26012d +0x18213ba0d029a5 +0x2c7 +0x21e +0x3cd +0x2ed +0x1c9 +0x12eeb282e67311 +0x70f801cad570 +0x0 +0xe7431464d3755 +0x1033f1de5f67a0 +0x58 +0x1535a55a6d00d +0xeec21f4e4462d +0x4 +0x74c5911718c5f +0xcde3a5429d3bf +0x8 +0x11b45fdd094ebb +0x1f167cee04ae61 +0xffffffffffffffff +0x17a28518b52445 +0x3321f47533362 +0x0 +0xdd01a125c4a4a +0x6a89a8d9ab90b +0x3 +0xe1049754f4420 +0x94c9cca847000 +0x2 +0x163568c0c286a3 +0x320875f898519 +0x1 +0xd65f1429c43f8 +0x19d7d707113edd +0x20f +0x2c9 +0x4f +0x860cd5aab781b +0x39b3c9375f97d +0x0 +0x2333e3d7ca98d +0x17a6d7616fae5 +0x0 +0x1be31c31fa1b65 +0x1e9cf0eff97714 +0x3b2 +0x1c5 +0x228d962252c1 +0x1da1342813d162 +0xe8 +0xcb52be9526514 +0xbe7715933fd26 +0x8 +0x8f1a4b72402b3 +0xd29274e087de2 +0xa +0x16eaf729826089 +0x18c0985d643ec9 +0x3eb +0x3af +0xef +0x771448beb2b60 +0xca48c76e6a43b +0x14 +0x12e8d395659fca +0x6962f0ecdd7eb +0x5 +0x1b5b9a7947347f +0x111eac80e7a6a +0x0 +0xa2f9593345b1a +0x23ab2574ece76 +0x0 +0x52bdb9f49038 +0x13c1e91774246c +0x109 +0x11678fb01f0e94 +0x1eed50c572ce61 +0x276 +0x96 +0x85293dd2b81d9 +0x46f5a55a32f21 +0x2 +0x8ea156b2403b8 +0x130c376c244b7a +0xea +0x593dbf28c8642 +0x48fb750754798 +0x0 +0x218b6e19d2ce7 +0x28667e2348614 +0x0 +0xb47c81a484822 +0xe7763abc2530e +0x3e +0x11621e6432f25e +0xb6e51a3c7540d +0x5 +0x15a8205db3e4cf +0x1f528a70225267 +0xffffffffffffffff +0x7f1a01494e0e2 +0x64a97840199f5 +0x0 +0xe8a7648610a60 +0x7814e573acea1 +0x2 +0x9f52042f72a1f +0xceeb054adc0f0 +0x11 +0x1f8e17539ad7cf +0x726edc3a875f6 +0x5 +0x14704edc0975f7 +0x118b2f49f75478 +0x4b +0x1bc318c2f02b8e +0x158d0ef586448e +0x2a1 +0x381 +0x136 +0x10bd503772dde5 +0x11f23f5e7b3f97 +0xac +0x184032fc1d0289 +0x112711dd817d83 +0x18 +0x273b20abf615c +0x1a1f72d887b0c2 +0x221 +0x58 +0x75447e86e9506 +0x1f5077c5158403 +0xffffffffffffffff +0x1484c44b895db2 +0x1a7a4cd63fe6fd +0x2d8 +0x3a7 +0x1be +0x49d80aad06826 +0xb75b3c47ca70b +0x7 +0x17b50ef6547c08 +0x61a34e6ccfe60 +0x3 +0x17c0e7eed36e10 +0x6a094e5ea11f2 +0x3 +0xe6e5d341e0f9f +0x18519b4701487e +0x3fb +0x72 +0xf64409ba1a52b +0x7c54c8de9c737 +0x6 +0x3bd3309d85dfa +0xd7c26e0c06e7f +0x2 +0x10a3874da0fe24 +0x17ee1a5fd6e736 +0x367 +0x25e +0x1aa +0xedea7935cd0ab +0xbce43cf730e22 +0xd +0x189100661f2b80 +0xbf03d117400cd +0xd +0x1940aba500afec +0xf0331a24e3663 +0x6 +0xbf51d6f2c634e +0x135a22a6c9173a +0x1d +0x6082f4e73edc4 +0x1e76c6b2e83524 +0x82 +0x23d8833c0cfd1 +0xad6c5e891d077 +0xf +0x1395753897997a +0x12a341871f9b19 +0xc4 +0x1af8256ea1b399 +0x1e023f88c2f3d1 +0x21f +0x1de +0x15e381df69a2fb +0x36dbfaad03710 +0x0 +0x1636516372e7b1 +0x2f1ee347aed69 +0x1 +0xb3da16c4564fc +0xc3cbf6936777d +0xa +0xa747baaa63b10 +0x5fd09cf3fd7ae +0x3 +0x841b987647c93 +0x14ee7c5e1c4aec +0x378 +0x310 +0x196 +0x86138cb44b38e +0x11ce7c42224c97 +0x91 +0x199672e5f8fe30 +0x19f254a45c1ad8 +0xf1 +0x93cbdc9747fab +0xd9f690e1b64d0 +0x0 +0x1ab1e9f7ba4a +0x59e57af077bea +0x2 +0x1c885e18521117 +0x81245406d707c +0x5 +0x1c09c2947a1067 +0x13215075ebb743 +0xe5 +0x1fc0b792b6c8ce +0xe9884cf04721d +0x2f +0x144617c620a489 +0x1f497e7115f5b4 +0xffffffffffffffff +0x1ae47ced5ad3a3 +0xa2c6fbd42a883 +0x5 +0xb9e9253823be +0x1bd67da246b7a6 +0x10e +0x1bb16c7f546078 +0x1edc5424565bbe +0x21c +0x305 +0x309 +0x94 +0x72cc716d0d736 +0x1e7c78aceae3e8 +0x124 +0xd0bf7d6644188 +0x133d77cb1a83e7 +0x92 +0x139da06e6cbf55 +0x1ca3de51b16a13 +0x19b +0x194a80ee239485 +0x1704d2bf665acd +0x3f4 +0x229 +0x31 +0x30bb2986d48e0 +0x6dad9af00bae1 +0x3 +0x143d3ccf72cc6a +0x1272257645e054 +0x84 +0x1124a37048f083 +0x1084ce7caad86a +0x9 +0x1cea54a09c4bb5 +0x1ab6eac00ea1cc +0xc1 +0x13c855d1666b97 +0x1da18afcac568a +0x273 +0x1fe +0x134dcf198d08d5 +0x56d7aa877e65b +0x3 +0x19c1af5ea21ea0 +0xeaca78e7822 +0x1 +0x807e8630dc7ed +0xdb91f876d1fe5 +0x16 +0xdd5c5f42f155 +0x129a02e8915eef +0x2 +0x365fdd52f9d57 +0x15660795ef0ed4 +0x217 +0x88 +0xdc1d746a07de2 +0x1e4a5c76a57b57 +0x198 +0x13b94df18444f1 +0xbb1f76a846da0 +0xb +0x17bb74a9f969e6 +0x42d5523f10d7a +0x1 +0x105046a138d532 +0x30621e8844265 +0x1 \ No newline at end of file diff --git a/go.mod b/go.mod index 16051cab5..3f9a3a431 100644 --- a/go.mod +++ b/go.mod @@ -240,3 +240,5 @@ require ( lukechampine.com/blake3 v1.2.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect ) + +replace github.com/livepeer/lpms => /home/ricks/development/livepeer/ai/lpms From 71089b3349764f5d7c0ef5e679b668f516cabe9f Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Wed, 31 Jul 2024 11:14:14 +0200 Subject: [PATCH 145/203] feat(ai): add minLivepeerVersion constraint and IgnorePreReleaseVersions - Adds minLivepeerVersion constraint from https://github.com/livepeer/go-livepeer/pull/3050 to the AI codebase. - Introduces `IgnorePreReleaseVersions` flag to filter out pre-release versions. This update is essential for distinguishing AI subnet versions, which use pre-release suffixes, from standard transcoding releases. This new flag can be removed when merging in the main branch. --- cmd/livepeer/livepeer.go | 1 + cmd/livepeer/starter/starter.go | 168 +++++++++---------- core/capabilities.go | 32 +++- go.mod | 20 ++- go.sum | 36 ++--- net/lp_rpc.pb.go | 277 +++++++++++++++++--------------- net/lp_rpc.proto | 1 + net/lp_rpc_grpc.pb.go | 122 ++++++++------ server/ai_session.go | 2 + server/broadcast.go | 1 + server/mediaserver.go | 1 + 11 files changed, 357 insertions(+), 304 deletions(-) diff --git a/cmd/livepeer/livepeer.go b/cmd/livepeer/livepeer.go index 2653e55a6..474175812 100755 --- a/cmd/livepeer/livepeer.go +++ b/cmd/livepeer/livepeer.go @@ -128,6 +128,7 @@ func parseLivepeerConfig() starter.LivepeerConfig { cfg.OrchWebhookURL = flag.String("orchWebhookUrl", *cfg.OrchWebhookURL, "Orchestrator discovery callback URL") cfg.OrchBlacklist = flag.String("orchBlocklist", "", "Comma-separated list of blocklisted orchestrators") cfg.OrchMinLivepeerVersion = flag.String("orchMinLivepeerVersion", *cfg.OrchMinLivepeerVersion, "Minimal go-livepeer version orchestrator should have to be selected") + cfg.IgnorePreReleaseVersions = flag.Bool("ignorePreReleaseVersions", *cfg.IgnorePreReleaseVersions, "Ignore pre-release version suffix when validating the minimum go-livepeer version (e.g., v0.7.5-beta, v0.7.5-0.20231004073737-06f1f383fb18)") cfg.SelectRandWeight = flag.Float64("selectRandFreq", *cfg.SelectRandWeight, "Weight of the random factor in the orchestrator selection algorithm") cfg.SelectStakeWeight = flag.Float64("selectStakeWeight", *cfg.SelectStakeWeight, "Weight of the stake factor in the orchestrator selection algorithm") cfg.SelectPriceWeight = flag.Float64("selectPriceWeight", *cfg.SelectPriceWeight, "Weight of the price factor in the orchestrator selection algorithm") diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index 6f9a11e0e..f4a2a2212 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -78,84 +78,85 @@ const ( ) type LivepeerConfig struct { - Network *string - RtmpAddr *string - CliAddr *string - HttpAddr *string - ServiceAddr *string - OrchAddr *string - VerifierURL *string - EthController *string - VerifierPath *string - LocalVerify *bool - HttpIngest *bool - Orchestrator *bool - Transcoder *bool - AIWorker *bool - Gateway *bool - Broadcaster *bool - OrchSecret *string - TranscodingOptions *string - AIModels *string - MaxAttempts *int - SelectRandWeight *float64 - SelectStakeWeight *float64 - SelectPriceWeight *float64 - SelectPriceExpFactor *float64 - OrchPerfStatsURL *string - Region *string - MaxPricePerUnit *string - MinPerfScore *float64 - MaxSessions *string - CurrentManifest *bool - Nvidia *string - Netint *string - TestTranscoder *bool - EthAcctAddr *string - EthPassword *string - EthKeystorePath *string - EthOrchAddr *string - EthUrl *string - TxTimeout *time.Duration - MaxTxReplacements *int - GasLimit *int - MinGasPrice *int64 - MaxGasPrice *int - InitializeRound *bool - InitializeRoundMaxDelay *time.Duration - TicketEV *string - MaxFaceValue *string - MaxTicketEV *string - MaxTotalEV *string - DepositMultiplier *int - PricePerUnit *string - PixelsPerUnit *string - PriceFeedAddr *string - AutoAdjustPrice *bool - PricePerGateway *string - PricePerBroadcaster *string - BlockPollingInterval *int - Redeemer *bool - RedeemerAddr *string - Reward *bool - Monitor *bool - MetricsPerStream *bool - MetricsExposeClientIP *bool - MetadataQueueUri *string - MetadataAmqpExchange *string - MetadataPublishTimeout *time.Duration - Datadir *string - AIModelsDir *string - Objectstore *string - Recordstore *string - FVfailGsBucket *string - FVfailGsKey *string - AuthWebhookURL *string - OrchWebhookURL *string - OrchBlacklist *string - OrchMinLivepeerVersion *string - TestOrchAvail *bool - AIRunnerImage *string + Network *string + RtmpAddr *string + CliAddr *string + HttpAddr *string + ServiceAddr *string + OrchAddr *string + VerifierURL *string + EthController *string + VerifierPath *string + LocalVerify *bool + HttpIngest *bool + Orchestrator *bool + Transcoder *bool + AIWorker *bool + Gateway *bool + Broadcaster *bool + OrchSecret *string + TranscodingOptions *string + AIModels *string + MaxAttempts *int + SelectRandWeight *float64 + SelectStakeWeight *float64 + SelectPriceWeight *float64 + SelectPriceExpFactor *float64 + OrchPerfStatsURL *string + Region *string + MaxPricePerUnit *string + MinPerfScore *float64 + MaxSessions *string + CurrentManifest *bool + Nvidia *string + Netint *string + TestTranscoder *bool + EthAcctAddr *string + EthPassword *string + EthKeystorePath *string + EthOrchAddr *string + EthUrl *string + TxTimeout *time.Duration + MaxTxReplacements *int + GasLimit *int + MinGasPrice *int64 + MaxGasPrice *int + InitializeRound *bool + InitializeRoundMaxDelay *time.Duration + TicketEV *string + MaxFaceValue *string + MaxTicketEV *string + MaxTotalEV *string + DepositMultiplier *int + PricePerUnit *string + PixelsPerUnit *string + PriceFeedAddr *string + AutoAdjustPrice *bool + PricePerGateway *string + PricePerBroadcaster *string + BlockPollingInterval *int + Redeemer *bool + RedeemerAddr *string + Reward *bool + Monitor *bool + MetricsPerStream *bool + MetricsExposeClientIP *bool + MetadataQueueUri *string + MetadataAmqpExchange *string + MetadataPublishTimeout *time.Duration + Datadir *string + AIModelsDir *string + Objectstore *string + Recordstore *string + FVfailGsBucket *string + FVfailGsKey *string + AuthWebhookURL *string + OrchWebhookURL *string + OrchBlacklist *string + OrchMinLivepeerVersion *string + IgnorePreReleaseVersions *bool + TestOrchAvail *bool + AIRunnerImage *string } // DefaultLivepeerConfig creates LivepeerConfig exactly the same as when no flags are passed to the livepeer process. @@ -250,6 +251,7 @@ func DefaultLivepeerConfig() LivepeerConfig { defaultAuthWebhookURL := "" defaultOrchWebhookURL := "" defaultMinLivepeerVersion := "" + defaultIgnorePreReleaseVersions := true // Flags defaultTestOrchAvail := true @@ -342,9 +344,12 @@ func DefaultLivepeerConfig() LivepeerConfig { FVfailGsKey: &defaultFVfailGsKey, // API - AuthWebhookURL: &defaultAuthWebhookURL, - OrchWebhookURL: &defaultOrchWebhookURL, - OrchMinLivepeerVersion: &defaultMinLivepeerVersion, + AuthWebhookURL: &defaultAuthWebhookURL, + OrchWebhookURL: &defaultOrchWebhookURL, + + // Versioning constraints + OrchMinLivepeerVersion: &defaultMinLivepeerVersion, + IgnorePreReleaseVersions: &defaultIgnorePreReleaseVersions, // Flags TestOrchAvail: &defaultTestOrchAvail, @@ -1379,6 +1384,7 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { n.Capabilities = core.NewCapabilitiesWithConstraints(append(transcoderCaps, aiCaps...), core.MandatoryOCapabilities(), core.Constraints{}, capabilityConstraints) if cfg.OrchMinLivepeerVersion != nil { n.Capabilities.SetMinVersionConstraint(*cfg.OrchMinLivepeerVersion) + n.Capabilities.SetIgnorePreReleaseVersions(*cfg.IgnorePreReleaseVersions) } if drivers.NodeStorage == nil { diff --git a/core/capabilities.go b/core/capabilities.go index 4f4122ec7..1fd6ee2f9 100644 --- a/core/capabilities.go +++ b/core/capabilities.go @@ -21,7 +21,8 @@ type ModelConstraint struct { type Capability int type CapabilityString []uint64 type Constraints struct { - minVersion string + minVersion string + ignorePreReleaseVersions bool } type PerCapabilityConstraints struct { // Models contains a *ModelConstraint for each supported model ID @@ -405,11 +406,15 @@ func (bcast *Capabilities) LivepeerVersionCompatibleWith(orch *net.Capabilities) return false } - // Ignore prerelease versions as in go-livepeer we actually define post-release suffixes - minVerNoSuffix, _ := minVer.SetPrerelease("") - verNoSuffix, _ := ver.SetPrerelease("") + // By default ignore prerelease versions as in go-livepeer we actually define post-release suffixes + if bcast.constraints.ignorePreReleaseVersions { + minVerNoSuffix, _ := minVer.SetPrerelease("") + verNoSuffix, _ := ver.SetPrerelease("") + minVer = &minVerNoSuffix + ver = &verNoSuffix + } - return !verNoSuffix.LessThan(&minVerNoSuffix) + return !ver.LessThan(minVer) } func (bcast *Capabilities) CompatibleWith(orch *net.Capabilities) bool { @@ -450,7 +455,7 @@ func (c *Capabilities) ToNetCapabilities() *net.Capabilities { } c.mutex.Lock() defer c.mutex.Unlock() - netCaps := &net.Capabilities{Bitstring: c.bitstring, Mandatories: c.mandatories, Version: c.version, Capacities: make(map[uint32]uint32), Constraints: &net.Capabilities_Constraints{MinVersion: c.constraints.minVersion}, CapabilityConstraints: make(map[uint32]*net.Capabilities_CapabilityConstraints)} + netCaps := &net.Capabilities{Bitstring: c.bitstring, Mandatories: c.mandatories, Version: c.version, Capacities: make(map[uint32]uint32), Constraints: &net.Capabilities_Constraints{MinVersion: c.constraints.minVersion, IgnorePreReleaseVersions: c.constraints.ignorePreReleaseVersions}, CapabilityConstraints: make(map[uint32]*net.Capabilities_CapabilityConstraints)} for capability, capacity := range c.capacities { netCaps.Capacities[uint32(capability)] = uint32(capacity) } @@ -478,7 +483,7 @@ func CapabilitiesFromNetCapabilities(caps *net.Capabilities) *Capabilities { mandatories: caps.Mandatories, capacities: make(map[Capability]int), version: caps.Version, - constraints: Constraints{minVersion: caps.Constraints.GetMinVersion()}, + constraints: Constraints{minVersion: caps.Constraints.GetMinVersion(), ignorePreReleaseVersions: caps.Constraints.GetIgnorePreReleaseVersions()}, capabilityConstraints: make(CapabilityConstraints), } if caps.Capacities == nil || len(caps.Capacities) == 0 { @@ -718,3 +723,16 @@ func (bcast *Capabilities) MinVersionConstraint() string { } return "" } + +func (bcast *Capabilities) SetIgnorePreReleaseVersions(ignorePreReleaseVersions bool) { + if bcast != nil { + bcast.constraints.ignorePreReleaseVersions = ignorePreReleaseVersions + } +} + +func (bcast *Capabilities) IgnorePreReleaseVersions() bool { + if bcast != nil { + return bcast.constraints.ignorePreReleaseVersions + } + return false +} diff --git a/go.mod b/go.mod index 3f9a3a431..52fcd8672 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/cenkalti/backoff v2.2.1+incompatible github.com/ethereum/go-ethereum v1.13.5 github.com/getkin/kin-openapi v0.124.0 - github.com/golang/glog v1.1.1 + github.com/golang/glog v1.2.1 github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.4 github.com/jaypipes/ghw v0.10.0 @@ -32,15 +32,14 @@ require ( go.opencensus.io v0.24.0 go.uber.org/goleak v1.3.0 golang.org/x/net v0.25.0 - google.golang.org/grpc v1.57.1 - google.golang.org/protobuf v1.33.0 + google.golang.org/grpc v1.65.0 + google.golang.org/protobuf v1.34.1 pgregory.net/rapid v1.1.0 ) require ( cloud.google.com/go v0.110.2 // indirect - cloud.google.com/go/compute v1.20.0 // indirect - cloud.google.com/go/compute/metadata v0.2.3 // indirect + cloud.google.com/go/compute/metadata v0.3.0 // indirect cloud.google.com/go/iam v1.1.0 // indirect cloud.google.com/go/storage v1.30.1 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect @@ -54,7 +53,7 @@ require ( github.com/bits-and-blooms/bitset v1.7.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect github.com/cespare/cp v1.1.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cockroachdb/errors v1.8.1 // indirect github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f // indirect github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 // indirect @@ -102,10 +101,9 @@ require ( github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect - github.com/google/go-cmp v0.6.0 // indirect github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect github.com/google/s2a-go v0.1.4 // indirect - github.com/google/uuid v1.5.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect github.com/googleapis/gax-go/v2 v2.10.0 // indirect github.com/gorilla/mux v1.8.1 // indirect @@ -220,7 +218,7 @@ require ( golang.org/x/crypto v0.23.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/oauth2 v0.8.0 // indirect + golang.org/x/oauth2 v0.20.0 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.20.0 // indirect golang.org/x/text v0.15.0 // indirect @@ -230,8 +228,8 @@ require ( google.golang.org/api v0.125.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index e4c680fbc..fee65b0c5 100644 --- a/go.sum +++ b/go.sum @@ -21,10 +21,8 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.20.0 h1:cUOcywWuowO9It2i1KX1lIb0HH7gLv6nENKuZGnlcSo= -cloud.google.com/go/compute v1.20.0/go.mod h1:kn5BhC++qUWR/AM3Dn21myV7QbgqejW04cAOrtppaQI= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= +cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/iam v1.1.0 h1:67gSqaPukx7O8WLLHMa0PNs3EBGd2eE4d+psbO/CO94= @@ -111,8 +109,9 @@ github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= @@ -319,8 +318,8 @@ github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.1.1 h1:jxpi2eWoU84wbX9iIEyAeeoac3FLuifZpY9tcNUD9kw= -github.com/golang/glog v1.1.1/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= +github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= +github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -396,8 +395,8 @@ github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= -github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -1116,8 +1115,8 @@ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= +golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1354,10 +1353,10 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao= google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= -google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM= -google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -1376,8 +1375,8 @@ google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTp google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.57.1 h1:upNTNqv0ES+2ZOOqACwVtS3Il8M12/+Hz41RCPzAjQg= -google.golang.org/grpc v1.57.1/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1393,8 +1392,9 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/net/lp_rpc.pb.go b/net/lp_rpc.pb.go index 70a6f4b1f..772e30d1e 100644 --- a/net/lp_rpc.pb.go +++ b/net/lp_rpc.pb.go @@ -339,9 +339,8 @@ func (m *OrchestratorRequest) GetCapabilities() *Capabilities { return nil } -// -//OSInfo needed to negotiate storages that will be used. -//It carries info needed to write to the storage. +// OSInfo needed to negotiate storages that will be used. +// It carries info needed to write to the storage. type OSInfo struct { // Storage type: direct, s3, ipfs. StorageType OSInfo_StorageType `protobuf:"varint,1,opt,name=storageType,proto3,enum=net.OSInfo_StorageType" json:"storageType,omitempty"` @@ -610,10 +609,11 @@ func (m *Capabilities) GetCapabilityConstraints() map[uint32]*Capabilities_Capab // Non-binary general constraints. type Capabilities_Constraints struct { - MinVersion string `protobuf:"bytes,1,opt,name=minVersion,proto3" json:"minVersion,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + MinVersion string `protobuf:"bytes,1,opt,name=minVersion,proto3" json:"minVersion,omitempty"` + IgnorePreReleaseVersions bool `protobuf:"varint,2,opt,name=ignorePreReleaseVersions,proto3" json:"ignorePreReleaseVersions,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Capabilities_Constraints) Reset() { *m = Capabilities_Constraints{} } @@ -648,6 +648,13 @@ func (m *Capabilities_Constraints) GetMinVersion() string { return "" } +func (m *Capabilities_Constraints) GetIgnorePreReleaseVersions() bool { + if m != nil { + return m.IgnorePreReleaseVersions + } + return false +} + // Non-binary capability constraints, such as supported ranges. type Capabilities_CapabilityConstraints struct { Models map[string]*Capabilities_CapabilityConstraints_ModelConstraint `protobuf:"bytes,1,rep,name=models,proto3" json:"models,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` @@ -1369,6 +1376,7 @@ type TranscodeResult struct { // Result of transcoding can be an error, or successful with more info // // Types that are valid to be assigned to Result: + // // *TranscodeResult_Error // *TranscodeResult_Data Result isTranscodeResult_Result `protobuf_oneof:"result"` @@ -1909,132 +1917,133 @@ func init() { } var fileDescriptor_034e29c79f9ba827 = []byte{ - // 2021 bytes of a gzipped FileDescriptorProto + // 2041 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x58, 0xdd, 0x72, 0xdb, 0xc6, - 0xf5, 0x17, 0x3f, 0xc4, 0x8f, 0x43, 0x52, 0x82, 0xd6, 0x96, 0x0c, 0x33, 0x76, 0xfe, 0x32, 0x12, - 0xe5, 0xef, 0xcc, 0xd4, 0x8a, 0x87, 0x92, 0x9d, 0xb8, 0x33, 0x99, 0x56, 0x1f, 0xb4, 0xc4, 0xd4, - 0x92, 0x38, 0x4b, 0xd9, 0x33, 0xed, 0x45, 0x59, 0x08, 0x58, 0x92, 0xa8, 0x48, 0x00, 0x5a, 0x2c, - 0x63, 0x29, 0xd3, 0x17, 0xe8, 0x23, 0xb4, 0x37, 0x9d, 0xe9, 0x4c, 0x9f, 0xa0, 0x2f, 0xd0, 0x07, - 0xe8, 0x03, 0xf4, 0x41, 0x7a, 0xdf, 0xce, 0x9e, 0x5d, 0x80, 0x80, 0x48, 0x27, 0xaa, 0xef, 0xf6, - 0x7c, 0xee, 0xd9, 0xb3, 0x67, 0x7f, 0xe7, 0x00, 0x60, 0xf8, 0x4c, 0x7c, 0x35, 0x0e, 0xfb, 0x3c, - 0x74, 0xb6, 0x43, 0x1e, 0x88, 0x80, 0x14, 0x7c, 0x26, 0xac, 0x4d, 0xa8, 0x74, 0x3d, 0x7f, 0xd8, - 0x0d, 0xfc, 0x21, 0xb9, 0x0f, 0xcb, 0xdf, 0xdb, 0xe3, 0x29, 0x33, 0x73, 0x9b, 0xb9, 0xa7, 0x75, - 0xaa, 0x08, 0xeb, 0x04, 0x1e, 0xb5, 0x7d, 0xf7, 0x9c, 0xdb, 0x7e, 0xe4, 0x04, 0xae, 0xe7, 0x0f, - 0x7b, 0x2c, 0x8a, 0xbc, 0xc0, 0xa7, 0xec, 0x6a, 0xca, 0x22, 0x41, 0x9e, 0x01, 0xd8, 0x53, 0x31, - 0xea, 0x8b, 0xe0, 0x92, 0xf9, 0x68, 0x5a, 0x6b, 0xad, 0x6c, 0xfb, 0x4c, 0x6c, 0xef, 0x4d, 0xc5, - 0xe8, 0x5c, 0x72, 0x69, 0xd5, 0x8e, 0x97, 0xd6, 0xff, 0xc1, 0xe3, 0x0f, 0xb8, 0x8b, 0xc2, 0xc0, - 0x8f, 0x98, 0x75, 0x0d, 0xf7, 0xce, 0xb8, 0x33, 0x62, 0x91, 0xe0, 0xb6, 0x08, 0x78, 0xbc, 0x8d, - 0x09, 0x65, 0xdb, 0x75, 0x39, 0x8b, 0x22, 0x1d, 0x5e, 0x4c, 0x12, 0x03, 0x0a, 0x91, 0x37, 0x34, - 0xf3, 0xc8, 0x95, 0x4b, 0xf2, 0x02, 0xea, 0x8e, 0x1d, 0xda, 0x17, 0xde, 0xd8, 0x13, 0x1e, 0x8b, - 0xcc, 0x02, 0x06, 0xb5, 0x86, 0x41, 0x1d, 0xa4, 0x04, 0x34, 0xa3, 0x66, 0xfd, 0x29, 0x07, 0xa5, - 0xb3, 0x5e, 0xc7, 0x1f, 0x04, 0xe4, 0x15, 0xd4, 0x22, 0x11, 0x70, 0x7b, 0xc8, 0xce, 0x6f, 0x42, - 0x95, 0x90, 0x95, 0xd6, 0x03, 0x74, 0xa0, 0x34, 0xb6, 0x7b, 0x33, 0x31, 0x4d, 0xeb, 0x92, 0x2d, - 0x28, 0x45, 0x3b, 0x9e, 0x3f, 0x08, 0x4c, 0x03, 0xb7, 0x6d, 0xa0, 0x55, 0x6f, 0x47, 0xd9, 0x51, - 0x2d, 0xb4, 0x9e, 0x41, 0x2d, 0xe5, 0x82, 0x00, 0x94, 0x0e, 0x3b, 0xb4, 0x7d, 0x70, 0x6e, 0x2c, - 0x91, 0x12, 0xe4, 0x7b, 0x3b, 0x46, 0x4e, 0xf2, 0x8e, 0xce, 0xce, 0x8e, 0xde, 0xb4, 0x8d, 0xbc, - 0xf5, 0xd7, 0x1c, 0x54, 0x62, 0x1f, 0x84, 0x40, 0x71, 0x14, 0x44, 0x02, 0xc3, 0xaa, 0x52, 0x5c, - 0xcb, 0x2c, 0x5c, 0xb2, 0x1b, 0xcc, 0x42, 0x95, 0xca, 0x25, 0xd9, 0x80, 0x52, 0x18, 0x8c, 0x3d, - 0xe7, 0x06, 0xcf, 0x5f, 0xa5, 0x9a, 0x22, 0x8f, 0xa0, 0x1a, 0x79, 0x43, 0xdf, 0x16, 0x53, 0xce, - 0xcc, 0x22, 0x8a, 0x66, 0x0c, 0xf2, 0x29, 0x80, 0xc3, 0x99, 0xcb, 0x7c, 0xe1, 0xd9, 0x63, 0x73, - 0x19, 0xc5, 0x29, 0x0e, 0x69, 0x42, 0xe5, 0x7a, 0x6f, 0xf2, 0xc3, 0xa1, 0x2d, 0x98, 0x59, 0x42, - 0x69, 0x42, 0x5b, 0x6f, 0xa1, 0xda, 0xe5, 0x9e, 0xc3, 0x30, 0x48, 0x0b, 0xea, 0xa1, 0x24, 0xba, - 0x8c, 0xbf, 0xf5, 0x3d, 0x15, 0x6c, 0x81, 0x66, 0x78, 0xe4, 0x73, 0x68, 0x84, 0xde, 0x35, 0x1b, - 0x47, 0xb1, 0x52, 0x1e, 0x95, 0xb2, 0x4c, 0xeb, 0xef, 0x25, 0xa8, 0xa7, 0xaf, 0x4d, 0x9e, 0xe0, - 0xc2, 0x13, 0x91, 0xe0, 0x9e, 0x3f, 0x34, 0x73, 0x9b, 0x85, 0xa7, 0x45, 0x3a, 0x63, 0x90, 0x4d, - 0xa8, 0x4d, 0x6c, 0xdf, 0x95, 0xc5, 0x23, 0x2f, 0x3f, 0x8f, 0xf2, 0x34, 0x8b, 0xec, 0x01, 0xc8, - 0x8b, 0x77, 0xe2, 0xea, 0x28, 0x3c, 0xad, 0xb5, 0x9e, 0xcc, 0x55, 0x07, 0x12, 0x4a, 0xa7, 0xed, - 0x0b, 0x7e, 0x43, 0x53, 0x46, 0xb2, 0x1c, 0xbf, 0x67, 0x5c, 0x16, 0xae, 0x4e, 0x61, 0x4c, 0x92, - 0x5f, 0x40, 0xcd, 0x09, 0x7c, 0x59, 0xbd, 0x9e, 0x2f, 0x22, 0xcc, 0x60, 0xad, 0xf5, 0x78, 0x81, - 0xf7, 0x99, 0x12, 0x4d, 0x5b, 0x90, 0x0b, 0x58, 0x4f, 0xca, 0xf2, 0x26, 0xa5, 0x65, 0x96, 0x30, - 0xd0, 0x9f, 0x2d, 0x0e, 0x74, 0x4e, 0x5d, 0xc5, 0xbc, 0xd8, 0x55, 0xf3, 0x5b, 0x58, 0xbd, 0x75, - 0xba, 0xb8, 0x80, 0xe4, 0x35, 0x35, 0x54, 0x01, 0x25, 0x78, 0x90, 0x47, 0x9e, 0x22, 0x7e, 0x9e, - 0xff, 0x26, 0xd7, 0x7c, 0x06, 0xb5, 0x94, 0x37, 0x59, 0x33, 0x13, 0xcf, 0x7f, 0xa7, 0xf3, 0xa1, - 0xaa, 0x32, 0xc5, 0x69, 0xfe, 0x27, 0x07, 0xeb, 0x0b, 0x63, 0x24, 0xbf, 0x82, 0xd2, 0x24, 0x70, - 0xd9, 0x38, 0xc2, 0x6b, 0xac, 0xb5, 0x76, 0xee, 0x78, 0xb8, 0xed, 0x13, 0xb4, 0x52, 0x67, 0xd4, - 0x2e, 0x9a, 0x5b, 0xb0, 0x8a, 0xec, 0x99, 0x9e, 0x7c, 0x29, 0xef, 0x6d, 0x3e, 0xc1, 0x98, 0x2a, - 0x14, 0xd7, 0x4d, 0x0e, 0xb5, 0x94, 0x75, 0xfa, 0xdc, 0xfa, 0xe1, 0x9c, 0xa4, 0xcf, 0x5d, 0x6b, - 0x7d, 0xfd, 0x3f, 0xc5, 0x34, 0x63, 0xa4, 0x13, 0x76, 0x05, 0xcd, 0x0f, 0x5f, 0xd2, 0x82, 0xd4, - 0x7f, 0x9b, 0x0d, 0xe1, 0xff, 0xef, 0x18, 0x42, 0x6a, 0x4b, 0xeb, 0x1f, 0x79, 0x30, 0xd2, 0x40, - 0x8a, 0x8f, 0xf2, 0x53, 0x00, 0xa1, 0xa1, 0x97, 0xf1, 0xf8, 0xa6, 0x66, 0x1c, 0xf2, 0x12, 0x1a, - 0xc2, 0x73, 0x2e, 0x99, 0xe8, 0x87, 0x36, 0xb7, 0x27, 0x91, 0xde, 0x5f, 0x41, 0xe7, 0x39, 0x4a, - 0xba, 0x28, 0xa0, 0x75, 0x91, 0xa2, 0x64, 0x13, 0xc0, 0x87, 0xdd, 0x47, 0xe0, 0x2b, 0xa4, 0x9a, - 0x40, 0x02, 0x08, 0xb4, 0x1a, 0x26, 0xd8, 0x90, 0x02, 0xf3, 0x62, 0x16, 0xcc, 0x6f, 0x43, 0xf7, - 0xf2, 0x9d, 0xa0, 0xfb, 0x56, 0x13, 0x2a, 0xfd, 0x44, 0x13, 0x22, 0x5b, 0x50, 0xd6, 0x90, 0x6d, - 0x6e, 0x62, 0xdd, 0xd5, 0x52, 0xd0, 0x4e, 0x63, 0x99, 0xf5, 0x3b, 0xa8, 0x26, 0xe6, 0xf2, 0x35, - 0xcc, 0x5a, 0x5c, 0x9d, 0x2a, 0x82, 0x3c, 0x06, 0x88, 0x54, 0x03, 0xeb, 0x7b, 0xae, 0x46, 0xdf, - 0xaa, 0xe6, 0x74, 0x5c, 0x99, 0x6f, 0x76, 0x1d, 0x7a, 0xdc, 0x16, 0xf2, 0x65, 0x14, 0x10, 0xdd, - 0x52, 0x1c, 0xeb, 0xdf, 0x45, 0x28, 0xf7, 0xd8, 0xf0, 0xd0, 0x16, 0x36, 0xbe, 0x22, 0xdb, 0xf7, - 0x06, 0x2c, 0x12, 0x1d, 0x57, 0xef, 0x92, 0xe2, 0x60, 0x9f, 0x63, 0x57, 0x1a, 0x22, 0xe5, 0x12, - 0xfb, 0x80, 0x1d, 0x8d, 0xd0, 0x6f, 0x9d, 0xe2, 0x5a, 0xe2, 0x73, 0xc8, 0x83, 0x81, 0x37, 0x66, - 0x71, 0x6e, 0x13, 0x3a, 0xee, 0x94, 0xcb, 0xb3, 0x4e, 0xd9, 0x84, 0x8a, 0x3b, 0xd5, 0xd1, 0xc9, - 0xac, 0x2d, 0xd3, 0x84, 0x9e, 0xbb, 0x8a, 0xf2, 0xc7, 0x5c, 0x45, 0xe5, 0xa7, 0xae, 0xe2, 0x39, - 0xdc, 0x77, 0xec, 0xb1, 0xd3, 0x0f, 0x19, 0x77, 0x58, 0x28, 0xa6, 0xf6, 0xb8, 0x8f, 0x67, 0x02, - 0x7c, 0xb1, 0x44, 0xca, 0xba, 0x89, 0xe8, 0x58, 0x9e, 0xf0, 0x6e, 0x97, 0x27, 0xc3, 0x1f, 0x4c, - 0xc7, 0xe3, 0x6e, 0x9c, 0x8c, 0x27, 0xa8, 0xab, 0xc2, 0x7f, 0xe7, 0xb9, 0x2c, 0xd0, 0x12, 0x9a, - 0x51, 0x23, 0x5f, 0x43, 0x23, 0x4d, 0xb7, 0x4c, 0xeb, 0x43, 0x76, 0x59, 0xbd, 0xdb, 0x86, 0x3b, - 0xe6, 0x67, 0x77, 0x32, 0xdc, 0x21, 0x7b, 0x40, 0x22, 0x36, 0x9c, 0x30, 0x5f, 0x3f, 0x3a, 0x26, - 0x18, 0x8f, 0xcc, 0x2d, 0x4c, 0x1c, 0x51, 0xc3, 0x03, 0x1b, 0x76, 0x13, 0x09, 0x5d, 0xd3, 0xda, - 0x33, 0x16, 0xd9, 0x06, 0xf2, 0x3a, 0xe0, 0x0e, 0x4b, 0x66, 0x29, 0x4f, 0x36, 0xd3, 0x2f, 0x54, - 0x0a, 0xe7, 0x25, 0xd6, 0x0e, 0x34, 0x32, 0x3e, 0x65, 0x25, 0x0d, 0x78, 0xa0, 0x70, 0xb2, 0x48, - 0x71, 0x4d, 0x56, 0x20, 0x2f, 0x02, 0x2c, 0xb7, 0x22, 0xcd, 0x8b, 0xc0, 0xfa, 0xe7, 0x32, 0xd4, - 0xd3, 0xe7, 0x90, 0x46, 0xbe, 0x3d, 0x61, 0x38, 0xe7, 0x54, 0x29, 0xae, 0xe5, 0x2b, 0x79, 0xef, - 0xb9, 0x62, 0x64, 0xae, 0x61, 0x35, 0x29, 0x42, 0x8e, 0x22, 0x23, 0xe6, 0x0d, 0x47, 0xc2, 0x24, - 0xc8, 0xd6, 0x94, 0xc4, 0x81, 0x0b, 0x4f, 0xc2, 0x13, 0x33, 0xef, 0xa1, 0x20, 0x26, 0x65, 0xa9, - 0x0e, 0xc2, 0xc8, 0xbc, 0xaf, 0x20, 0x71, 0x10, 0x46, 0xe4, 0x39, 0x94, 0x06, 0x01, 0x9f, 0xd8, - 0xc2, 0x5c, 0xc7, 0x69, 0xcc, 0x9c, 0x4b, 0xec, 0xf6, 0x6b, 0x94, 0x53, 0xad, 0x27, 0x77, 0x1d, - 0x84, 0xd1, 0x21, 0xf3, 0xcd, 0x0d, 0x74, 0xa3, 0x29, 0xb2, 0x03, 0x65, 0xfd, 0x24, 0xcc, 0x07, - 0xe8, 0xea, 0xe1, 0xbc, 0xab, 0xf8, 0xae, 0x62, 0x4d, 0x19, 0xd0, 0x30, 0x08, 0x4d, 0x13, 0xc3, - 0x94, 0x4b, 0xf2, 0x12, 0xca, 0xcc, 0x57, 0x40, 0xfa, 0x10, 0xdd, 0x3c, 0x9a, 0x77, 0x83, 0xc4, - 0x41, 0xe0, 0x32, 0x87, 0xc6, 0xca, 0x38, 0x61, 0x05, 0xe3, 0x80, 0x1f, 0xb2, 0x50, 0x8c, 0xcc, - 0x26, 0x3a, 0x4c, 0x71, 0xc8, 0x11, 0xd4, 0x9d, 0x11, 0x0f, 0x26, 0xb6, 0x3a, 0x8e, 0xf9, 0x09, - 0x3a, 0xff, 0x6c, 0xde, 0xf9, 0x01, 0x6a, 0xf5, 0xa6, 0x17, 0x91, 0x3d, 0x09, 0xc7, 0x9e, 0x3f, - 0xa4, 0x19, 0x43, 0x99, 0xdd, 0xab, 0xa9, 0x2d, 0x5b, 0x84, 0xf9, 0x08, 0x13, 0x10, 0x93, 0xd6, - 0x63, 0x28, 0x69, 0x1d, 0x80, 0xd2, 0x49, 0xb7, 0x7d, 0x74, 0xde, 0x33, 0x96, 0x48, 0x19, 0x0a, - 0x27, 0xdd, 0x5d, 0x23, 0x67, 0xfd, 0x1e, 0xca, 0xf1, 0x1d, 0xdf, 0x83, 0xd5, 0xf6, 0xe9, 0xc1, - 0xd9, 0x61, 0x9b, 0xf6, 0x0f, 0xdb, 0xaf, 0xf7, 0xde, 0xbe, 0x91, 0x03, 0xea, 0x1a, 0x34, 0x8e, - 0x5b, 0x2f, 0x77, 0xfb, 0xfb, 0x7b, 0xbd, 0xf6, 0x9b, 0xce, 0x69, 0xdb, 0xc8, 0x91, 0x06, 0x54, - 0x91, 0x75, 0xb2, 0xd7, 0x39, 0x35, 0xf2, 0x09, 0x79, 0xdc, 0x39, 0x3a, 0x36, 0x0a, 0xe4, 0x21, - 0xac, 0x23, 0x79, 0x70, 0x76, 0xda, 0x3b, 0xa7, 0x7b, 0x9d, 0xd3, 0xf6, 0xa1, 0x12, 0x15, 0xad, - 0x16, 0xc0, 0x2c, 0x49, 0xa4, 0x02, 0x45, 0xa9, 0x68, 0x2c, 0xe9, 0xd5, 0x0b, 0x23, 0x27, 0xc3, - 0x7a, 0xd7, 0xfd, 0xc6, 0xc8, 0xab, 0xc5, 0x2b, 0xa3, 0x60, 0x1d, 0xc0, 0xda, 0xdc, 0xd9, 0xc9, - 0x0a, 0xc0, 0xc1, 0x31, 0x3d, 0x3b, 0xd9, 0xeb, 0xef, 0xb6, 0x9e, 0x1b, 0x4b, 0x19, 0xba, 0x65, - 0xe4, 0xd2, 0xf4, 0xee, 0xae, 0x91, 0xb7, 0xae, 0x60, 0x3d, 0xfe, 0x0a, 0x61, 0x6e, 0x4f, 0x3d, - 0x29, 0xc4, 0x61, 0x03, 0x0a, 0x53, 0x3e, 0x8e, 0x07, 0x82, 0x29, 0x1f, 0xe3, 0x24, 0x8d, 0x13, - 0xa9, 0x06, 0x5f, 0x4d, 0x91, 0x6d, 0xb8, 0x77, 0x0b, 0xb6, 0xfa, 0xd2, 0x52, 0x8d, 0xdb, 0x6b, - 0x61, 0x06, 0xb6, 0xde, 0xf2, 0xb1, 0xf5, 0x6b, 0x68, 0x24, 0x5b, 0xe2, 0x56, 0x2f, 0xa1, 0xa2, - 0x1f, 0x73, 0x3c, 0x00, 0x35, 0x55, 0xa7, 0x5d, 0x14, 0x18, 0x4d, 0x74, 0xe7, 0x3f, 0x79, 0xac, - 0x3f, 0xe7, 0x60, 0x35, 0xb1, 0xa2, 0x2c, 0x9a, 0x8e, 0x45, 0xdc, 0x30, 0x72, 0xb3, 0x86, 0xb1, - 0x01, 0xcb, 0x8c, 0xf3, 0x80, 0xab, 0x46, 0x75, 0xbc, 0x44, 0x15, 0x49, 0x9e, 0x42, 0xd1, 0xb5, - 0x85, 0xad, 0x1b, 0x37, 0xc9, 0xc6, 0x20, 0xf7, 0x3e, 0x5e, 0xa2, 0xa8, 0x41, 0xbe, 0x84, 0x62, - 0xea, 0xdb, 0x66, 0x5d, 0x21, 0xef, 0xad, 0x29, 0x83, 0xa2, 0xca, 0x7e, 0x05, 0x4a, 0x1c, 0x03, - 0xb1, 0xfe, 0x00, 0xab, 0x94, 0x0d, 0xbd, 0x48, 0xb0, 0xe4, 0x73, 0x6e, 0x03, 0x4a, 0x11, 0x73, - 0x38, 0x8b, 0x3f, 0x62, 0x34, 0x25, 0x1b, 0x92, 0x9e, 0xb2, 0x6f, 0x74, 0xb2, 0x13, 0xfa, 0x63, - 0x3f, 0xeb, 0xfe, 0x98, 0x83, 0xc6, 0x69, 0x20, 0xbc, 0xc1, 0x8d, 0x4e, 0xe6, 0x82, 0x1b, 0xfe, - 0x02, 0xca, 0x91, 0x6a, 0xc3, 0xda, 0x6b, 0x3d, 0x06, 0x5e, 0xcc, 0x7c, 0x2c, 0x94, 0x61, 0x0b, - 0x3b, 0xba, 0xec, 0xb8, 0x98, 0x80, 0x02, 0xd5, 0x54, 0xa6, 0xeb, 0xae, 0x65, 0xbb, 0xee, 0x77, - 0xc5, 0x4a, 0xde, 0x28, 0x7c, 0x57, 0xac, 0x3c, 0x31, 0x2c, 0xeb, 0x2f, 0x79, 0xa8, 0xa7, 0xc7, - 0x28, 0xf9, 0x29, 0xc3, 0x99, 0xe3, 0x85, 0x1e, 0xf3, 0x85, 0xee, 0xf9, 0x33, 0x86, 0x9c, 0x2e, - 0x06, 0xb6, 0xc3, 0xfa, 0xb3, 0x59, 0xb0, 0x4e, 0xab, 0x92, 0xf3, 0x4e, 0x32, 0xc8, 0x43, 0xa8, - 0xbc, 0xf7, 0xfc, 0x7e, 0xc8, 0x83, 0x0b, 0x3d, 0x03, 0x94, 0xdf, 0x7b, 0x7e, 0x97, 0x07, 0x17, - 0xb2, 0x34, 0x13, 0x37, 0x7d, 0x6e, 0xfb, 0xae, 0xea, 0xaa, 0x6a, 0x22, 0x58, 0x4b, 0x44, 0xd4, - 0xf6, 0x5d, 0x6c, 0xaa, 0x04, 0x8a, 0x11, 0x63, 0xae, 0x9e, 0x0d, 0x70, 0x4d, 0xbe, 0x04, 0x63, - 0x36, 0xaa, 0xf4, 0x2f, 0xc6, 0x81, 0x73, 0x89, 0x43, 0x42, 0x9d, 0xae, 0xce, 0xf8, 0xfb, 0x92, - 0x4d, 0x8e, 0x61, 0x2d, 0xa5, 0xaa, 0x67, 0x47, 0x35, 0x30, 0x7c, 0x92, 0x9a, 0x1d, 0xdb, 0x89, - 0x8e, 0x9e, 0x22, 0x53, 0x1b, 0x28, 0x8e, 0xd5, 0x01, 0xa2, 0x74, 0x7b, 0xcc, 0x77, 0x19, 0xd7, - 0x69, 0x7a, 0x02, 0xf5, 0x08, 0xe9, 0xbe, 0x1f, 0xf8, 0x0e, 0xd3, 0xa3, 0x72, 0x4d, 0xf1, 0x4e, - 0x25, 0x6b, 0xc1, 0x9b, 0xf8, 0x01, 0x36, 0x16, 0x6f, 0x4b, 0xb6, 0x60, 0xc5, 0xe1, 0x4c, 0x05, - 0xcb, 0x83, 0xa9, 0xef, 0xea, 0x47, 0xd2, 0x88, 0xb9, 0x54, 0x32, 0xc9, 0x2b, 0x78, 0x98, 0x55, - 0x53, 0x49, 0x50, 0xa9, 0x54, 0x1b, 0x6d, 0x64, 0x2c, 0x30, 0x19, 0x32, 0x9f, 0xd6, 0xdf, 0xf2, - 0x50, 0xee, 0xda, 0x37, 0x58, 0x6e, 0x73, 0x43, 0x75, 0xee, 0x6e, 0x43, 0x35, 0xbe, 0x11, 0x79, - 0x40, 0xbd, 0x97, 0xa6, 0x16, 0x27, 0xbb, 0xf0, 0x11, 0xc9, 0x26, 0x1d, 0xb8, 0xaf, 0x23, 0xd3, - 0xd9, 0xd5, 0xce, 0x8a, 0x88, 0x45, 0x0f, 0x52, 0xce, 0xd2, 0xb7, 0x41, 0x89, 0x98, 0xbf, 0xa1, - 0x17, 0xb0, 0xc2, 0xae, 0x43, 0xe6, 0x08, 0xe6, 0xf6, 0x71, 0xd0, 0xd7, 0xa3, 0xfb, 0xed, 0xaf, - 0x80, 0x46, 0xac, 0x85, 0xac, 0xd6, 0xbf, 0x72, 0x50, 0x4f, 0xe3, 0x07, 0xd9, 0x87, 0xd5, 0x23, - 0x26, 0x32, 0x2c, 0x73, 0x0e, 0x65, 0x34, 0x8a, 0x34, 0x17, 0xe3, 0x0f, 0xf9, 0x2d, 0xac, 0x2f, - 0xfc, 0xc7, 0x44, 0xd4, 0x47, 0xfe, 0x8f, 0xfd, 0xce, 0x6a, 0x5a, 0x3f, 0xa6, 0xa2, 0x7e, 0x51, - 0x91, 0xcf, 0xa1, 0xd8, 0x95, 0x2d, 0x47, 0xfd, 0xda, 0x89, 0xff, 0x9f, 0x35, 0xb3, 0x64, 0xeb, - 0x14, 0xe0, 0x7c, 0xf6, 0x65, 0xf5, 0x4b, 0x20, 0x31, 0x06, 0xa6, 0xb8, 0xf7, 0xd1, 0xe4, 0x16, - 0x38, 0x36, 0x15, 0x00, 0x67, 0x30, 0xeb, 0x79, 0x6e, 0xbf, 0xfc, 0x9b, 0xe5, 0xed, 0xaf, 0x7c, - 0x26, 0x2e, 0x4a, 0xf8, 0xff, 0x6e, 0xe7, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xa2, 0xc3, 0xc1, - 0x2e, 0xd3, 0x13, 0x00, 0x00, + 0x15, 0x16, 0x7f, 0xc4, 0x9f, 0x43, 0x52, 0x82, 0xd6, 0x96, 0x0c, 0x33, 0x76, 0x2a, 0x23, 0x51, + 0xea, 0xcc, 0x34, 0x8a, 0x87, 0x92, 0x9d, 0x38, 0x33, 0x99, 0x56, 0x3f, 0xb4, 0xc4, 0xd4, 0x92, + 0x38, 0x4b, 0xd9, 0x33, 0xed, 0x45, 0x59, 0x08, 0x58, 0x92, 0xa8, 0x40, 0x00, 0x5a, 0x2c, 0x63, + 0x29, 0xd3, 0x17, 0xe8, 0x23, 0xb4, 0x37, 0x9d, 0xe9, 0x4c, 0xdf, 0xa3, 0x0f, 0xd0, 0x07, 0xe8, + 0x5b, 0xf4, 0xa6, 0xf7, 0xed, 0xec, 0xd9, 0x05, 0x08, 0x8a, 0x74, 0xa2, 0xfa, 0x6e, 0xcf, 0xef, + 0x9e, 0x3d, 0x7b, 0xf6, 0x3b, 0x07, 0x00, 0x23, 0x60, 0xe2, 0x4b, 0x3f, 0xea, 0xf3, 0xc8, 0xd9, + 0x8e, 0x78, 0x28, 0x42, 0x52, 0x08, 0x98, 0xb0, 0x36, 0xa1, 0xd2, 0xf5, 0x82, 0x61, 0x37, 0x0c, + 0x86, 0xe4, 0x3e, 0x2c, 0x7f, 0x6f, 0xfb, 0x13, 0x66, 0xe6, 0x36, 0x73, 0x4f, 0xeb, 0x54, 0x11, + 0xd6, 0x09, 0x3c, 0x6a, 0x07, 0xee, 0x39, 0xb7, 0x83, 0xd8, 0x09, 0x5d, 0x2f, 0x18, 0xf6, 0x58, + 0x1c, 0x7b, 0x61, 0x40, 0xd9, 0xd5, 0x84, 0xc5, 0x82, 0x7c, 0x01, 0x60, 0x4f, 0xc4, 0xa8, 0x2f, + 0xc2, 0x4b, 0x16, 0xa0, 0x69, 0xad, 0xb5, 0xb2, 0x1d, 0x30, 0xb1, 0xbd, 0x37, 0x11, 0xa3, 0x73, + 0xc9, 0xa5, 0x55, 0x3b, 0x59, 0x5a, 0x3f, 0x83, 0xc7, 0xef, 0x71, 0x17, 0x47, 0x61, 0x10, 0x33, + 0xeb, 0x1a, 0xee, 0x9d, 0x71, 0x67, 0xc4, 0x62, 0xc1, 0x6d, 0x11, 0xf2, 0x64, 0x1b, 0x13, 0xca, + 0xb6, 0xeb, 0x72, 0x16, 0xc7, 0x3a, 0xbc, 0x84, 0x24, 0x06, 0x14, 0x62, 0x6f, 0x68, 0xe6, 0x91, + 0x2b, 0x97, 0xe4, 0x39, 0xd4, 0x1d, 0x3b, 0xb2, 0x2f, 0x3c, 0xdf, 0x13, 0x1e, 0x8b, 0xcd, 0x02, + 0x06, 0xb5, 0x86, 0x41, 0x1d, 0x64, 0x04, 0x74, 0x46, 0xcd, 0xfa, 0x73, 0x0e, 0x4a, 0x67, 0xbd, + 0x4e, 0x30, 0x08, 0xc9, 0x4b, 0xa8, 0xc5, 0x22, 0xe4, 0xf6, 0x90, 0x9d, 0xdf, 0x44, 0x2a, 0x21, + 0x2b, 0xad, 0x07, 0xe8, 0x40, 0x69, 0x6c, 0xf7, 0xa6, 0x62, 0x9a, 0xd5, 0x25, 0x5b, 0x50, 0x8a, + 0x77, 0xbc, 0x60, 0x10, 0x9a, 0x06, 0x6e, 0xdb, 0x40, 0xab, 0xde, 0x8e, 0xb2, 0xa3, 0x5a, 0x68, + 0x7d, 0x01, 0xb5, 0x8c, 0x0b, 0x02, 0x50, 0x3a, 0xec, 0xd0, 0xf6, 0xc1, 0xb9, 0xb1, 0x44, 0x4a, + 0x90, 0xef, 0xed, 0x18, 0x39, 0xc9, 0x3b, 0x3a, 0x3b, 0x3b, 0x7a, 0xdd, 0x36, 0xf2, 0xd6, 0xdf, + 0x72, 0x50, 0x49, 0x7c, 0x10, 0x02, 0xc5, 0x51, 0x18, 0x0b, 0x0c, 0xab, 0x4a, 0x71, 0x2d, 0xb3, + 0x70, 0xc9, 0x6e, 0x30, 0x0b, 0x55, 0x2a, 0x97, 0x64, 0x03, 0x4a, 0x51, 0xe8, 0x7b, 0xce, 0x0d, + 0x9e, 0xbf, 0x4a, 0x35, 0x45, 0x1e, 0x41, 0x35, 0xf6, 0x86, 0x81, 0x2d, 0x26, 0x9c, 0x99, 0x45, + 0x14, 0x4d, 0x19, 0xe4, 0x63, 0x00, 0x87, 0x33, 0x97, 0x05, 0xc2, 0xb3, 0x7d, 0x73, 0x19, 0xc5, + 0x19, 0x0e, 0x69, 0x42, 0xe5, 0x7a, 0x6f, 0xfc, 0xc3, 0xa1, 0x2d, 0x98, 0x59, 0x42, 0x69, 0x4a, + 0x5b, 0x6f, 0xa0, 0xda, 0xe5, 0x9e, 0xc3, 0x30, 0x48, 0x0b, 0xea, 0x91, 0x24, 0xba, 0x8c, 0xbf, + 0x09, 0x3c, 0x15, 0x6c, 0x81, 0xce, 0xf0, 0xc8, 0xa7, 0xd0, 0x88, 0xbc, 0x6b, 0xe6, 0xc7, 0x89, + 0x52, 0x1e, 0x95, 0x66, 0x99, 0xd6, 0xbf, 0x4b, 0x50, 0xcf, 0x5e, 0x9b, 0x3c, 0xc1, 0x85, 0x27, + 0x62, 0xc1, 0xbd, 0x60, 0x68, 0xe6, 0x36, 0x0b, 0x4f, 0x8b, 0x74, 0xca, 0x20, 0x9b, 0x50, 0x1b, + 0xdb, 0x81, 0x2b, 0x8b, 0x47, 0x5e, 0x7e, 0x1e, 0xe5, 0x59, 0x16, 0xd9, 0x03, 0x90, 0x17, 0xef, + 0x24, 0xd5, 0x51, 0x78, 0x5a, 0x6b, 0x3d, 0x99, 0xab, 0x0e, 0x24, 0x94, 0x4e, 0x3b, 0x10, 0xfc, + 0x86, 0x66, 0x8c, 0x64, 0x39, 0x7e, 0xcf, 0xb8, 0x2c, 0x5c, 0x9d, 0xc2, 0x84, 0x24, 0xbf, 0x84, + 0x9a, 0x13, 0x06, 0xb2, 0x7a, 0xbd, 0x40, 0xc4, 0x98, 0xc1, 0x5a, 0xeb, 0xf1, 0x02, 0xef, 0x53, + 0x25, 0x9a, 0xb5, 0x20, 0x17, 0xb0, 0x9e, 0x96, 0xe5, 0x4d, 0x46, 0xcb, 0x2c, 0x61, 0xa0, 0xbf, + 0x58, 0x1c, 0xe8, 0x9c, 0xba, 0x8a, 0x79, 0xb1, 0xab, 0xe6, 0xb7, 0xb0, 0x7a, 0xeb, 0x74, 0x49, + 0x01, 0xc9, 0x6b, 0x6a, 0xa8, 0x02, 0x4a, 0xf1, 0x20, 0x8f, 0x3c, 0x45, 0x7c, 0x93, 0xff, 0x3a, + 0xd7, 0xf4, 0xa0, 0x96, 0xf1, 0x26, 0x6b, 0x66, 0xec, 0x05, 0x6f, 0x75, 0x3e, 0x54, 0x55, 0x66, + 0x38, 0xe4, 0x1b, 0x30, 0xbd, 0x61, 0x10, 0x72, 0xd6, 0xe5, 0x8c, 0x32, 0x9f, 0xd9, 0x31, 0xd3, + 0xa2, 0x18, 0x7d, 0x57, 0xe8, 0x7b, 0xe5, 0xcd, 0xff, 0xe6, 0x60, 0x7d, 0xe1, 0xf9, 0xc8, 0xaf, + 0xa1, 0x34, 0x0e, 0x5d, 0xe6, 0xc7, 0x58, 0x02, 0xb5, 0xd6, 0xce, 0x1d, 0x13, 0xb3, 0x7d, 0x82, + 0x56, 0x2a, 0x3f, 0xda, 0x45, 0x73, 0x0b, 0x56, 0x91, 0x3d, 0xd5, 0x93, 0xaf, 0xec, 0x9d, 0xcd, + 0xc7, 0x78, 0x9e, 0x0a, 0xc5, 0x75, 0x93, 0x43, 0x2d, 0x63, 0x9d, 0xcd, 0x99, 0x7e, 0x74, 0x27, + 0xd9, 0x9c, 0xd5, 0x5a, 0x5f, 0xfd, 0x5f, 0x31, 0x4d, 0x19, 0xd9, 0x64, 0x5f, 0x41, 0xf3, 0xfd, + 0x17, 0xbc, 0xe0, 0xda, 0xbe, 0x9d, 0x0d, 0xe1, 0xe7, 0x77, 0x0c, 0x21, 0xb3, 0xa5, 0xf5, 0x8f, + 0x3c, 0x18, 0x59, 0x10, 0xc6, 0x07, 0xfd, 0x31, 0x80, 0xd0, 0xb0, 0xcd, 0x78, 0x72, 0xcb, 0x53, + 0x0e, 0x79, 0x01, 0x0d, 0xe1, 0x39, 0x97, 0x4c, 0xf4, 0x23, 0x9b, 0xdb, 0xe3, 0x58, 0xef, 0xaf, + 0x60, 0xf7, 0x1c, 0x25, 0x5d, 0x14, 0xd0, 0xba, 0xc8, 0x50, 0xb2, 0x81, 0x20, 0x28, 0xf4, 0x11, + 0x34, 0x0b, 0x99, 0x06, 0x92, 0x82, 0x09, 0xad, 0x46, 0x29, 0xae, 0x64, 0x1a, 0x41, 0x71, 0xb6, + 0x11, 0xdc, 0x86, 0xfd, 0xe5, 0x3b, 0xc1, 0xfe, 0xad, 0x06, 0x56, 0xfa, 0x89, 0x06, 0x46, 0xb6, + 0xa0, 0xac, 0xe1, 0xde, 0xdc, 0xc4, 0xba, 0xab, 0x65, 0xda, 0x02, 0x4d, 0x64, 0xd6, 0xef, 0xa1, + 0x9a, 0x9a, 0xcb, 0x97, 0x34, 0x6d, 0x8f, 0x75, 0xaa, 0x08, 0xf2, 0x18, 0x20, 0x56, 0xcd, 0xaf, + 0xef, 0xb9, 0x1a, 0xb9, 0xab, 0x9a, 0xd3, 0x71, 0x65, 0xbe, 0xd9, 0x75, 0xe4, 0x71, 0x5b, 0xc8, + 0x57, 0x55, 0x40, 0x64, 0xcc, 0x70, 0xac, 0xff, 0x14, 0xa1, 0xdc, 0x63, 0xc3, 0x43, 0x5b, 0xd8, + 0xf8, 0x02, 0xed, 0xc0, 0x1b, 0xb0, 0x58, 0x74, 0x5c, 0xbd, 0x4b, 0x86, 0x83, 0x3d, 0x92, 0x5d, + 0x69, 0x78, 0x95, 0x4b, 0xec, 0x21, 0x76, 0x3c, 0x42, 0xbf, 0x75, 0x8a, 0x6b, 0x89, 0xed, 0x11, + 0x0f, 0x07, 0x9e, 0xcf, 0x92, 0xdc, 0xa6, 0x74, 0xd2, 0x65, 0x97, 0xa7, 0x5d, 0xb6, 0x09, 0x15, + 0x77, 0xa2, 0xa3, 0x93, 0x59, 0x5b, 0xa6, 0x29, 0x3d, 0x77, 0x15, 0xe5, 0x0f, 0xb9, 0x8a, 0xca, + 0x4f, 0x5d, 0xc5, 0x33, 0xb8, 0xef, 0xd8, 0xbe, 0xd3, 0x8f, 0x18, 0x77, 0x58, 0x24, 0x26, 0xb6, + 0xdf, 0xc7, 0x33, 0x01, 0xbe, 0x58, 0x22, 0x65, 0xdd, 0x54, 0x74, 0x2c, 0x4f, 0x78, 0xb7, 0xcb, + 0x93, 0xe1, 0x0f, 0x26, 0xbe, 0xdf, 0x4d, 0x92, 0xf1, 0x04, 0x75, 0x55, 0xf8, 0x6f, 0x3d, 0x97, + 0x85, 0x5a, 0x42, 0x67, 0xd4, 0xc8, 0x57, 0xd0, 0xc8, 0xd2, 0x2d, 0xd3, 0x7a, 0x9f, 0xdd, 0xac, + 0xde, 0x6d, 0xc3, 0x1d, 0xf3, 0x93, 0x3b, 0x19, 0xee, 0x90, 0x3d, 0x20, 0x31, 0x1b, 0x8e, 0x59, + 0xa0, 0x1f, 0x1d, 0x13, 0x8c, 0xc7, 0xe6, 0x16, 0x26, 0x8e, 0xa8, 0xc1, 0x83, 0x0d, 0xbb, 0xa9, + 0x84, 0xae, 0x69, 0xed, 0x29, 0x8b, 0x6c, 0x03, 0x79, 0x15, 0x72, 0x87, 0xa5, 0x73, 0x98, 0x27, + 0x1b, 0xf1, 0x67, 0x2a, 0x85, 0xf3, 0x12, 0x6b, 0x07, 0x1a, 0x33, 0x3e, 0x65, 0x25, 0x0d, 0x78, + 0xa8, 0x70, 0xb2, 0x48, 0x71, 0x4d, 0x56, 0x20, 0x2f, 0x42, 0x2c, 0xb7, 0x22, 0xcd, 0x8b, 0xd0, + 0xfa, 0xe7, 0x32, 0xd4, 0xb3, 0xe7, 0x90, 0x46, 0x81, 0x3d, 0x66, 0x38, 0x23, 0x55, 0x29, 0xae, + 0xe5, 0x2b, 0x79, 0xe7, 0xb9, 0x62, 0x64, 0xae, 0x61, 0x35, 0x29, 0x42, 0x8e, 0x31, 0x23, 0xe6, + 0x0d, 0x47, 0xc2, 0x24, 0xc8, 0xd6, 0x94, 0xc4, 0x81, 0x0b, 0x4f, 0xc2, 0x13, 0x33, 0xef, 0xa1, + 0x20, 0x21, 0x65, 0xa9, 0x0e, 0xa2, 0xd8, 0xbc, 0xaf, 0x20, 0x71, 0x10, 0xc5, 0xe4, 0x19, 0x94, + 0x06, 0x21, 0x1f, 0xdb, 0xc2, 0x5c, 0xc7, 0x49, 0xce, 0x9c, 0x4b, 0xec, 0xf6, 0x2b, 0x94, 0x53, + 0xad, 0x27, 0x77, 0x1d, 0x44, 0xf1, 0x21, 0x0b, 0xcc, 0x0d, 0x74, 0xa3, 0x29, 0xb2, 0x03, 0x65, + 0xfd, 0x24, 0xcc, 0x07, 0xe8, 0xea, 0xe1, 0xbc, 0xab, 0xe4, 0xae, 0x12, 0x4d, 0x19, 0xd0, 0x30, + 0x8c, 0x4c, 0x13, 0xc3, 0x94, 0x4b, 0xf2, 0x02, 0xca, 0x2c, 0x50, 0x40, 0xfa, 0x10, 0xdd, 0x3c, + 0x9a, 0x77, 0x83, 0xc4, 0x41, 0xe8, 0x32, 0x87, 0x26, 0xca, 0x38, 0x9d, 0x85, 0x7e, 0xc8, 0x0f, + 0x59, 0x24, 0x46, 0x66, 0x13, 0x1d, 0x66, 0x38, 0xe4, 0x08, 0xea, 0xce, 0x88, 0x87, 0x63, 0x5b, + 0x1d, 0xc7, 0xfc, 0x08, 0x9d, 0x7f, 0x32, 0xef, 0xfc, 0x00, 0xb5, 0x7a, 0x93, 0x8b, 0xd8, 0x1e, + 0x47, 0xbe, 0x17, 0x0c, 0xe9, 0x8c, 0xa1, 0xcc, 0xee, 0xd5, 0xc4, 0x96, 0x2d, 0xc2, 0x7c, 0x84, + 0x09, 0x48, 0x48, 0xeb, 0x31, 0x94, 0xb4, 0x0e, 0x40, 0xe9, 0xa4, 0xdb, 0x3e, 0x3a, 0xef, 0x19, + 0x4b, 0xa4, 0x0c, 0x85, 0x93, 0xee, 0xae, 0x91, 0xb3, 0xfe, 0x00, 0xe5, 0xe4, 0x8e, 0xef, 0xc1, + 0x6a, 0xfb, 0xf4, 0xe0, 0xec, 0xb0, 0x4d, 0xfb, 0x87, 0xed, 0x57, 0x7b, 0x6f, 0x5e, 0xcb, 0xe1, + 0x76, 0x0d, 0x1a, 0xc7, 0xad, 0x17, 0xbb, 0xfd, 0xfd, 0xbd, 0x5e, 0xfb, 0x75, 0xe7, 0xb4, 0x6d, + 0xe4, 0x48, 0x03, 0xaa, 0xc8, 0x3a, 0xd9, 0xeb, 0x9c, 0x1a, 0xf9, 0x94, 0x3c, 0xee, 0x1c, 0x1d, + 0x1b, 0x05, 0xf2, 0x10, 0xd6, 0x91, 0x3c, 0x38, 0x3b, 0xed, 0x9d, 0xd3, 0xbd, 0xce, 0x69, 0xfb, + 0x50, 0x89, 0x8a, 0x56, 0x0b, 0x60, 0x9a, 0x24, 0x52, 0x81, 0xa2, 0x54, 0x34, 0x96, 0xf4, 0xea, + 0xb9, 0x91, 0x93, 0x61, 0xbd, 0xed, 0x7e, 0x6d, 0xe4, 0xd5, 0xe2, 0xa5, 0x51, 0xb0, 0x0e, 0x60, + 0x6d, 0xee, 0xec, 0x64, 0x05, 0xe0, 0xe0, 0x98, 0x9e, 0x9d, 0xec, 0xf5, 0x77, 0x5b, 0xcf, 0x8c, + 0xa5, 0x19, 0xba, 0x65, 0xe4, 0xb2, 0xf4, 0xee, 0xae, 0x91, 0xb7, 0xae, 0x60, 0x3d, 0xf9, 0x82, + 0x61, 0x6e, 0x4f, 0x3d, 0x29, 0xc4, 0x61, 0x03, 0x0a, 0x13, 0xee, 0x27, 0x03, 0xc1, 0x84, 0xfb, + 0x38, 0x85, 0xe3, 0x34, 0xab, 0xc1, 0x57, 0x53, 0x64, 0x1b, 0xee, 0xdd, 0x82, 0xad, 0xbe, 0xb4, + 0x54, 0xa3, 0xfa, 0x5a, 0x34, 0x03, 0x5b, 0x6f, 0xb8, 0x6f, 0xfd, 0x06, 0x1a, 0xe9, 0x96, 0xb8, + 0xd5, 0x0b, 0xa8, 0xe8, 0xc7, 0x9c, 0x0c, 0x40, 0x4d, 0xd5, 0x69, 0x17, 0x05, 0x46, 0x53, 0xdd, + 0xf9, 0xcf, 0x25, 0xeb, 0x2f, 0x39, 0x58, 0x4d, 0xad, 0x28, 0x8b, 0x27, 0xbe, 0x48, 0x1a, 0x46, + 0x6e, 0xda, 0x30, 0x36, 0x60, 0x99, 0x71, 0x1e, 0x72, 0xd5, 0xa8, 0x8e, 0x97, 0xa8, 0x22, 0xc9, + 0x53, 0x28, 0xba, 0xb6, 0xb0, 0x75, 0xe3, 0x26, 0xb3, 0x31, 0xc8, 0xbd, 0x8f, 0x97, 0x28, 0x6a, + 0x90, 0xcf, 0xa1, 0x98, 0xf9, 0x2e, 0x5a, 0x57, 0xc8, 0x7b, 0x6b, 0xca, 0xa0, 0xa8, 0xb2, 0x5f, + 0x81, 0x12, 0xc7, 0x40, 0xac, 0x3f, 0xc2, 0x2a, 0x65, 0x43, 0x2f, 0x16, 0x2c, 0xfd, 0x14, 0xdc, + 0x80, 0x52, 0xcc, 0x1c, 0xce, 0x92, 0x0f, 0x20, 0x4d, 0xc9, 0x86, 0xa4, 0x27, 0xf4, 0x1b, 0x9d, + 0xec, 0x94, 0xfe, 0xd0, 0x4f, 0xc2, 0x3f, 0xe5, 0xa0, 0x71, 0x1a, 0x0a, 0x6f, 0x70, 0xa3, 0x93, + 0xb9, 0xe0, 0x86, 0x3f, 0x83, 0x72, 0xac, 0xda, 0xb0, 0xf6, 0x5a, 0x4f, 0x80, 0x17, 0x33, 0x9f, + 0x08, 0x65, 0xd8, 0xc2, 0x8e, 0x2f, 0x3b, 0x2e, 0x26, 0xa0, 0x40, 0x35, 0x35, 0xd3, 0x75, 0xd7, + 0x66, 0xbb, 0xee, 0x77, 0xc5, 0x4a, 0xde, 0x28, 0x7c, 0x57, 0xac, 0x3c, 0x31, 0x2c, 0xeb, 0xaf, + 0x79, 0xa8, 0x67, 0xc7, 0x28, 0xf9, 0x19, 0xc4, 0x99, 0xe3, 0x45, 0x1e, 0x0b, 0x84, 0xee, 0xf9, + 0x53, 0x86, 0x9c, 0x2e, 0x06, 0xb6, 0xc3, 0xfa, 0xd3, 0x59, 0xb0, 0x4e, 0xab, 0x92, 0xf3, 0x56, + 0x32, 0xc8, 0x43, 0xa8, 0xbc, 0xf3, 0x82, 0x7e, 0xc4, 0xc3, 0x0b, 0x3d, 0x03, 0x94, 0xdf, 0x79, + 0x41, 0x97, 0x87, 0x17, 0xb2, 0x34, 0x53, 0x37, 0x7d, 0x6e, 0x07, 0xae, 0xea, 0xaa, 0x6a, 0x22, + 0x58, 0x4b, 0x45, 0xd4, 0x0e, 0x5c, 0x6c, 0xaa, 0x04, 0x8a, 0x31, 0x63, 0xae, 0x9e, 0x0d, 0x70, + 0x4d, 0x3e, 0x07, 0x63, 0x3a, 0xaa, 0xf4, 0x2f, 0xfc, 0xd0, 0xb9, 0xc4, 0x21, 0xa1, 0x4e, 0x57, + 0xa7, 0xfc, 0x7d, 0xc9, 0x26, 0xc7, 0xb0, 0x96, 0x51, 0xd5, 0xb3, 0xa3, 0x1a, 0x18, 0x3e, 0xca, + 0xcc, 0x8e, 0xed, 0x54, 0x47, 0x4f, 0x91, 0x99, 0x0d, 0x14, 0xc7, 0xea, 0x00, 0x51, 0xba, 0x3d, + 0x16, 0xb8, 0x8c, 0xeb, 0x34, 0x3d, 0x81, 0x7a, 0x8c, 0x74, 0x3f, 0x08, 0x03, 0x87, 0xe9, 0x51, + 0xb9, 0xa6, 0x78, 0xa7, 0x92, 0xb5, 0xe0, 0x4d, 0xfc, 0x00, 0x1b, 0x8b, 0xb7, 0x25, 0x5b, 0xb0, + 0xe2, 0x70, 0xa6, 0x82, 0xe5, 0xe1, 0x24, 0x70, 0xf5, 0x23, 0x69, 0x24, 0x5c, 0x2a, 0x99, 0xe4, + 0x25, 0x3c, 0x9c, 0x55, 0x53, 0x49, 0x50, 0xa9, 0x54, 0x1b, 0x6d, 0xcc, 0x58, 0x60, 0x32, 0x64, + 0x3e, 0xad, 0xbf, 0xe7, 0xa1, 0xdc, 0xb5, 0x6f, 0xb0, 0xdc, 0xe6, 0x86, 0xea, 0xdc, 0xdd, 0x86, + 0x6a, 0x7c, 0x23, 0xf2, 0x80, 0x7a, 0x2f, 0x4d, 0x2d, 0x4e, 0x76, 0xe1, 0x03, 0x92, 0x4d, 0x3a, + 0x70, 0x5f, 0x47, 0xa6, 0xb3, 0xab, 0x9d, 0x15, 0x11, 0x8b, 0x1e, 0x64, 0x9c, 0x65, 0x6f, 0x83, + 0x12, 0x31, 0x7f, 0x43, 0xcf, 0x61, 0x85, 0x5d, 0x47, 0xcc, 0x11, 0xcc, 0xed, 0xe3, 0xa0, 0xaf, + 0x47, 0xf7, 0xdb, 0x5f, 0x01, 0x8d, 0x44, 0x0b, 0x59, 0xad, 0x7f, 0xe5, 0xa0, 0x9e, 0xc5, 0x0f, + 0xb2, 0x0f, 0xab, 0x47, 0x4c, 0xcc, 0xb0, 0xcc, 0x39, 0x94, 0xd1, 0x28, 0xd2, 0x5c, 0x8c, 0x3f, + 0xe4, 0x77, 0xb0, 0xbe, 0xf0, 0xff, 0x14, 0x51, 0x3f, 0x08, 0x7e, 0xec, 0x57, 0x58, 0xd3, 0xfa, + 0x31, 0x15, 0xf5, 0x7b, 0x8b, 0x7c, 0x0a, 0xc5, 0xae, 0x6c, 0x39, 0xea, 0xb7, 0x50, 0xf2, 0xef, + 0xad, 0x39, 0x4b, 0xb6, 0x4e, 0x01, 0xce, 0xa7, 0x5f, 0x56, 0xbf, 0x02, 0x92, 0x60, 0x60, 0x86, + 0x7b, 0x1f, 0x4d, 0x6e, 0x81, 0x63, 0x53, 0x01, 0xf0, 0x0c, 0x66, 0x3d, 0xcb, 0xed, 0x97, 0x7f, + 0xbb, 0xbc, 0xfd, 0x65, 0xc0, 0xc4, 0x45, 0x09, 0xff, 0xfd, 0xed, 0xfc, 0x2f, 0x00, 0x00, 0xff, + 0xff, 0x90, 0xe3, 0x7f, 0x0a, 0x0f, 0x14, 0x00, 0x00, } diff --git a/net/lp_rpc.proto b/net/lp_rpc.proto index bd3e6f1ef..d628df6a2 100644 --- a/net/lp_rpc.proto +++ b/net/lp_rpc.proto @@ -115,6 +115,7 @@ message Capabilities { // Non-binary general constraints. message Constraints { string minVersion = 1; + bool ignorePreReleaseVersions = 2; } // Non-binary capability constraints, such as supported ranges. diff --git a/net/lp_rpc_grpc.pb.go b/net/lp_rpc_grpc.pb.go index 5dd8d296b..fa1d80d2e 100644 --- a/net/lp_rpc_grpc.pb.go +++ b/net/lp_rpc_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.2.0 -// - protoc v3.12.4 +// - protoc-gen-go-grpc v1.5.1 +// - protoc v3.21.12 // source: net/lp_rpc.proto package net @@ -15,12 +15,20 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + Orchestrator_GetOrchestrator_FullMethodName = "/net.Orchestrator/GetOrchestrator" + Orchestrator_EndTranscodingSession_FullMethodName = "/net.Orchestrator/EndTranscodingSession" + Orchestrator_Ping_FullMethodName = "/net.Orchestrator/Ping" +) // OrchestratorClient is the client API for Orchestrator service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// RPC calls implemented by the orchestrator type OrchestratorClient interface { // Called by the broadcaster to request transcoder info from an orchestrator. GetOrchestrator(ctx context.Context, in *OrchestratorRequest, opts ...grpc.CallOption) (*OrchestratorInfo, error) @@ -37,8 +45,9 @@ func NewOrchestratorClient(cc grpc.ClientConnInterface) OrchestratorClient { } func (c *orchestratorClient) GetOrchestrator(ctx context.Context, in *OrchestratorRequest, opts ...grpc.CallOption) (*OrchestratorInfo, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(OrchestratorInfo) - err := c.cc.Invoke(ctx, "/net.Orchestrator/GetOrchestrator", in, out, opts...) + err := c.cc.Invoke(ctx, Orchestrator_GetOrchestrator_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -46,8 +55,9 @@ func (c *orchestratorClient) GetOrchestrator(ctx context.Context, in *Orchestrat } func (c *orchestratorClient) EndTranscodingSession(ctx context.Context, in *EndTranscodingSessionRequest, opts ...grpc.CallOption) (*EndTranscodingSessionResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(EndTranscodingSessionResponse) - err := c.cc.Invoke(ctx, "/net.Orchestrator/EndTranscodingSession", in, out, opts...) + err := c.cc.Invoke(ctx, Orchestrator_EndTranscodingSession_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -55,8 +65,9 @@ func (c *orchestratorClient) EndTranscodingSession(ctx context.Context, in *EndT } func (c *orchestratorClient) Ping(ctx context.Context, in *PingPong, opts ...grpc.CallOption) (*PingPong, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(PingPong) - err := c.cc.Invoke(ctx, "/net.Orchestrator/Ping", in, out, opts...) + err := c.cc.Invoke(ctx, Orchestrator_Ping_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -65,7 +76,9 @@ func (c *orchestratorClient) Ping(ctx context.Context, in *PingPong, opts ...grp // OrchestratorServer is the server API for Orchestrator service. // All implementations must embed UnimplementedOrchestratorServer -// for forward compatibility +// for forward compatibility. +// +// RPC calls implemented by the orchestrator type OrchestratorServer interface { // Called by the broadcaster to request transcoder info from an orchestrator. GetOrchestrator(context.Context, *OrchestratorRequest) (*OrchestratorInfo, error) @@ -74,9 +87,12 @@ type OrchestratorServer interface { mustEmbedUnimplementedOrchestratorServer() } -// UnimplementedOrchestratorServer must be embedded to have forward compatible implementations. -type UnimplementedOrchestratorServer struct { -} +// UnimplementedOrchestratorServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedOrchestratorServer struct{} func (UnimplementedOrchestratorServer) GetOrchestrator(context.Context, *OrchestratorRequest) (*OrchestratorInfo, error) { return nil, status.Errorf(codes.Unimplemented, "method GetOrchestrator not implemented") @@ -88,6 +104,7 @@ func (UnimplementedOrchestratorServer) Ping(context.Context, *PingPong) (*PingPo return nil, status.Errorf(codes.Unimplemented, "method Ping not implemented") } func (UnimplementedOrchestratorServer) mustEmbedUnimplementedOrchestratorServer() {} +func (UnimplementedOrchestratorServer) testEmbeddedByValue() {} // UnsafeOrchestratorServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to OrchestratorServer will @@ -97,6 +114,13 @@ type UnsafeOrchestratorServer interface { } func RegisterOrchestratorServer(s grpc.ServiceRegistrar, srv OrchestratorServer) { + // If the following call pancis, it indicates UnimplementedOrchestratorServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&Orchestrator_ServiceDesc, srv) } @@ -110,7 +134,7 @@ func _Orchestrator_GetOrchestrator_Handler(srv interface{}, ctx context.Context, } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/net.Orchestrator/GetOrchestrator", + FullMethod: Orchestrator_GetOrchestrator_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(OrchestratorServer).GetOrchestrator(ctx, req.(*OrchestratorRequest)) @@ -128,7 +152,7 @@ func _Orchestrator_EndTranscodingSession_Handler(srv interface{}, ctx context.Co } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/net.Orchestrator/EndTranscodingSession", + FullMethod: Orchestrator_EndTranscodingSession_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(OrchestratorServer).EndTranscodingSession(ctx, req.(*EndTranscodingSessionRequest)) @@ -146,7 +170,7 @@ func _Orchestrator_Ping_Handler(srv interface{}, ctx context.Context, dec func(i } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/net.Orchestrator/Ping", + FullMethod: Orchestrator_Ping_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(OrchestratorServer).Ping(ctx, req.(*PingPong)) @@ -178,13 +202,17 @@ var Orchestrator_ServiceDesc = grpc.ServiceDesc{ Metadata: "net/lp_rpc.proto", } +const ( + Transcoder_RegisterTranscoder_FullMethodName = "/net.Transcoder/RegisterTranscoder" +) + // TranscoderClient is the client API for Transcoder service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type TranscoderClient interface { // Called by the transcoder to register to an orchestrator. The orchestrator // notifies registered transcoders of segments as they come in. - RegisterTranscoder(ctx context.Context, in *RegisterRequest, opts ...grpc.CallOption) (Transcoder_RegisterTranscoderClient, error) + RegisterTranscoder(ctx context.Context, in *RegisterRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[NotifySegment], error) } type transcoderClient struct { @@ -195,12 +223,13 @@ func NewTranscoderClient(cc grpc.ClientConnInterface) TranscoderClient { return &transcoderClient{cc} } -func (c *transcoderClient) RegisterTranscoder(ctx context.Context, in *RegisterRequest, opts ...grpc.CallOption) (Transcoder_RegisterTranscoderClient, error) { - stream, err := c.cc.NewStream(ctx, &Transcoder_ServiceDesc.Streams[0], "/net.Transcoder/RegisterTranscoder", opts...) +func (c *transcoderClient) RegisterTranscoder(ctx context.Context, in *RegisterRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[NotifySegment], error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &Transcoder_ServiceDesc.Streams[0], Transcoder_RegisterTranscoder_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &transcoderRegisterTranscoderClient{stream} + x := &grpc.GenericClientStream[RegisterRequest, NotifySegment]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } @@ -210,41 +239,31 @@ func (c *transcoderClient) RegisterTranscoder(ctx context.Context, in *RegisterR return x, nil } -type Transcoder_RegisterTranscoderClient interface { - Recv() (*NotifySegment, error) - grpc.ClientStream -} - -type transcoderRegisterTranscoderClient struct { - grpc.ClientStream -} - -func (x *transcoderRegisterTranscoderClient) Recv() (*NotifySegment, error) { - m := new(NotifySegment) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type Transcoder_RegisterTranscoderClient = grpc.ServerStreamingClient[NotifySegment] // TranscoderServer is the server API for Transcoder service. // All implementations must embed UnimplementedTranscoderServer -// for forward compatibility +// for forward compatibility. type TranscoderServer interface { // Called by the transcoder to register to an orchestrator. The orchestrator // notifies registered transcoders of segments as they come in. - RegisterTranscoder(*RegisterRequest, Transcoder_RegisterTranscoderServer) error + RegisterTranscoder(*RegisterRequest, grpc.ServerStreamingServer[NotifySegment]) error mustEmbedUnimplementedTranscoderServer() } -// UnimplementedTranscoderServer must be embedded to have forward compatible implementations. -type UnimplementedTranscoderServer struct { -} +// UnimplementedTranscoderServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedTranscoderServer struct{} -func (UnimplementedTranscoderServer) RegisterTranscoder(*RegisterRequest, Transcoder_RegisterTranscoderServer) error { +func (UnimplementedTranscoderServer) RegisterTranscoder(*RegisterRequest, grpc.ServerStreamingServer[NotifySegment]) error { return status.Errorf(codes.Unimplemented, "method RegisterTranscoder not implemented") } func (UnimplementedTranscoderServer) mustEmbedUnimplementedTranscoderServer() {} +func (UnimplementedTranscoderServer) testEmbeddedByValue() {} // UnsafeTranscoderServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to TranscoderServer will @@ -254,6 +273,13 @@ type UnsafeTranscoderServer interface { } func RegisterTranscoderServer(s grpc.ServiceRegistrar, srv TranscoderServer) { + // If the following call pancis, it indicates UnimplementedTranscoderServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&Transcoder_ServiceDesc, srv) } @@ -262,21 +288,11 @@ func _Transcoder_RegisterTranscoder_Handler(srv interface{}, stream grpc.ServerS if err := stream.RecvMsg(m); err != nil { return err } - return srv.(TranscoderServer).RegisterTranscoder(m, &transcoderRegisterTranscoderServer{stream}) -} - -type Transcoder_RegisterTranscoderServer interface { - Send(*NotifySegment) error - grpc.ServerStream + return srv.(TranscoderServer).RegisterTranscoder(m, &grpc.GenericServerStream[RegisterRequest, NotifySegment]{ServerStream: stream}) } -type transcoderRegisterTranscoderServer struct { - grpc.ServerStream -} - -func (x *transcoderRegisterTranscoderServer) Send(m *NotifySegment) error { - return x.ServerStream.SendMsg(m) -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type Transcoder_RegisterTranscoderServer = grpc.ServerStreamingServer[NotifySegment] // Transcoder_ServiceDesc is the grpc.ServiceDesc for Transcoder service. // It's only intended for direct use with grpc.RegisterService, diff --git a/server/ai_session.go b/server/ai_session.go index 7ef1f00bb..ad1227808 100644 --- a/server/ai_session.go +++ b/server/ai_session.go @@ -298,6 +298,8 @@ func (sel *AISessionSelector) getSessions(ctx context.Context) ([]*BroadcastSess }, } caps := core.NewCapabilitiesWithConstraints(append(core.DefaultCapabilities(), sel.cap), nil, core.Constraints{}, capabilityConstraints) + caps.SetMinVersionConstraint(sel.node.Capabilities.MinVersionConstraint()) + caps.SetIgnorePreReleaseVersions(sel.node.Capabilities.IgnorePreReleaseVersions()) // Set numOrchs to the pool size so that discovery tries to find maximum # of compatible orchs within a timeout numOrchs := sel.node.OrchestratorPool.Size() diff --git a/server/broadcast.go b/server/broadcast.go index df854cc48..77fb4b2dc 100755 --- a/server/broadcast.go +++ b/server/broadcast.go @@ -452,6 +452,7 @@ func (bsm *BroadcastSessionsManager) shouldSkipVerification(sessions []*Broadcas func NewSessionManager(ctx context.Context, node *core.LivepeerNode, params *core.StreamParameters, sel BroadcastSessionsSelectorFactory) *BroadcastSessionsManager { if node.Capabilities != nil { params.Capabilities.SetMinVersionConstraint(node.Capabilities.MinVersionConstraint()) + params.Capabilities.SetIgnorePreReleaseVersions(node.Capabilities.IgnorePreReleaseVersions()) } var trustedPoolSize, untrustedPoolSize float64 if node.OrchestratorPool != nil { diff --git a/server/mediaserver.go b/server/mediaserver.go index 1e1b1be15..797f7dced 100644 --- a/server/mediaserver.go +++ b/server/mediaserver.go @@ -452,6 +452,7 @@ func endRTMPStreamHandler(s *LivepeerServer) func(url *url.URL, rtmpStrm stream. return func(url *url.URL, rtmpStrm stream.RTMPVideoStream) error { params := streamParams(rtmpStrm.AppData()) params.Capabilities.SetMinVersionConstraint(s.LivepeerNode.Capabilities.MinVersionConstraint()) + params.Capabilities.SetIgnorePreReleaseVersions(s.LivepeerNode.Capabilities.IgnorePreReleaseVersions()) if params == nil { return errMismatchedParams } From 2f953329f0eb4e6db09e0e49cb269f15897e5106 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Wed, 31 Jul 2024 11:41:07 +0200 Subject: [PATCH 146/203] fix(ai): fix offchain 'PriceFeedWatcher is not initialized' error This commit ensures that the `PriceFeedWatcher is not initialized` error is not thrown when the software is run in `offchain` mode. --- .gitignore | 3 +++ cmd/livepeer/starter/starter.go | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ec5f509ad..9d493d245 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,10 @@ *.dll *.so *.dylib + +# IDE files *.vscode +*.code-workspace # Test binary, build with `go test -c` *.test diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index f4a2a2212..8df207fc2 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -1124,7 +1124,12 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { panic(fmt.Errorf("'pricePerUnit' value specified for model '%v' in pipeline '%v' must be >= 0, provided %v", config.ModelID, config.Pipeline, config.PricePerUnit)) } pricePerPixel := new(big.Rat).Quo(pricePerUnit, pixelsPerUnit) - autoPrice, err := core.NewAutoConvertedPrice(currency, pricePerPixel, nil) + var autoPrice *core.AutoConvertedPrice + if *cfg.Network == "offchain" { + autoPrice = core.NewFixedPrice(pricePerPixel) + } else { + autoPrice, err = core.NewAutoConvertedPrice(currency, pricePerPixel, nil) + } if err != nil { panic(fmt.Errorf("error converting price: %v", err)) } From 74a84bc5f16626fc14f8d332930814797fb9306e Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Wed, 31 Jul 2024 12:03:14 +0200 Subject: [PATCH 147/203] test: add pre-release suffix min version tests This commit adds some tests to check if pre-release versions are correctly handled when filtering orchestrators based on minimum version. --- core/capabilities_test.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/core/capabilities_test.go b/core/capabilities_test.go index f4a905552..1bc7dfb0b 100644 --- a/core/capabilities_test.go +++ b/core/capabilities_test.go @@ -339,12 +339,29 @@ func TestCapability_CompatibleWithNetCap(t *testing.T) { orch.version = "0.4.0" assert.False(bcast.CompatibleWith(orch.ToNetCapabilities())) - // broadcaster is not compatible with orchestrator - the same version + // broadcaster is compatible with orchestrator - the same version orch = NewCapabilities(nil, nil) bcast = NewCapabilities(nil, nil) bcast.constraints.minVersion = "0.4.1" orch.version = "0.4.1" assert.True(bcast.CompatibleWith(orch.ToNetCapabilities())) + + // broadcaster is not compatible with orchestrator pre-release - old O's version + orch = NewCapabilities(nil, nil) + bcast = NewCapabilities(nil, nil) + bcast.constraints.minVersion = "0.4.1-0.21000000000000-06f1f383fb18" + bcast.constraints.ignorePreReleaseVersions = false + orch.version = "0.4.1-0.20000000000000-06f1f383fb18" + assert.False(bcast.CompatibleWith(orch.ToNetCapabilities())) + + // broadcaster is compatible with orchestrator pre-release - higher O's version + orch = NewCapabilities(nil, nil) + bcast = NewCapabilities(nil, nil) + bcast.constraints.minVersion = "0.4.1-0.20000000000000-06f1f383fb18" + bcast.constraints.ignorePreReleaseVersions = false + orch.version = "0.4.1-0.21000000000000-06f1f383fb18" + assert.True(bcast.CompatibleWith(orch.ToNetCapabilities())) + } func TestCapability_RoundTrip_Net(t *testing.T) { From 09e7ea5f2ab295b30e71effe79a55d5bc4254f1a Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Wed, 31 Jul 2024 12:10:24 +0200 Subject: [PATCH 148/203] feat: simplify pre-release version filter logic This commit removes the POC `ignorePreReleaseVersions` command line argument for logic in the `LivepeerVersionCompatibleWith` which ensures that pre-release versions are only checked when the gateway specifies as pre-release suffix in the `orchMinLivepeerVersion` command line argument. --- cmd/livepeer/livepeer.go | 1 - cmd/livepeer/starter/starter.go | 162 ++++++++++--------- core/capabilities.go | 25 +-- core/capabilities_test.go | 9 +- net/lp_rpc.pb.go | 271 +++++++++++++++----------------- net/lp_rpc.proto | 1 - server/ai_session.go | 1 - server/broadcast.go | 1 - server/mediaserver.go | 1 - 9 files changed, 223 insertions(+), 249 deletions(-) diff --git a/cmd/livepeer/livepeer.go b/cmd/livepeer/livepeer.go index 474175812..2653e55a6 100755 --- a/cmd/livepeer/livepeer.go +++ b/cmd/livepeer/livepeer.go @@ -128,7 +128,6 @@ func parseLivepeerConfig() starter.LivepeerConfig { cfg.OrchWebhookURL = flag.String("orchWebhookUrl", *cfg.OrchWebhookURL, "Orchestrator discovery callback URL") cfg.OrchBlacklist = flag.String("orchBlocklist", "", "Comma-separated list of blocklisted orchestrators") cfg.OrchMinLivepeerVersion = flag.String("orchMinLivepeerVersion", *cfg.OrchMinLivepeerVersion, "Minimal go-livepeer version orchestrator should have to be selected") - cfg.IgnorePreReleaseVersions = flag.Bool("ignorePreReleaseVersions", *cfg.IgnorePreReleaseVersions, "Ignore pre-release version suffix when validating the minimum go-livepeer version (e.g., v0.7.5-beta, v0.7.5-0.20231004073737-06f1f383fb18)") cfg.SelectRandWeight = flag.Float64("selectRandFreq", *cfg.SelectRandWeight, "Weight of the random factor in the orchestrator selection algorithm") cfg.SelectStakeWeight = flag.Float64("selectStakeWeight", *cfg.SelectStakeWeight, "Weight of the stake factor in the orchestrator selection algorithm") cfg.SelectPriceWeight = flag.Float64("selectPriceWeight", *cfg.SelectPriceWeight, "Weight of the price factor in the orchestrator selection algorithm") diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index 8df207fc2..5746314bb 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -78,85 +78,84 @@ const ( ) type LivepeerConfig struct { - Network *string - RtmpAddr *string - CliAddr *string - HttpAddr *string - ServiceAddr *string - OrchAddr *string - VerifierURL *string - EthController *string - VerifierPath *string - LocalVerify *bool - HttpIngest *bool - Orchestrator *bool - Transcoder *bool - AIWorker *bool - Gateway *bool - Broadcaster *bool - OrchSecret *string - TranscodingOptions *string - AIModels *string - MaxAttempts *int - SelectRandWeight *float64 - SelectStakeWeight *float64 - SelectPriceWeight *float64 - SelectPriceExpFactor *float64 - OrchPerfStatsURL *string - Region *string - MaxPricePerUnit *string - MinPerfScore *float64 - MaxSessions *string - CurrentManifest *bool - Nvidia *string - Netint *string - TestTranscoder *bool - EthAcctAddr *string - EthPassword *string - EthKeystorePath *string - EthOrchAddr *string - EthUrl *string - TxTimeout *time.Duration - MaxTxReplacements *int - GasLimit *int - MinGasPrice *int64 - MaxGasPrice *int - InitializeRound *bool - InitializeRoundMaxDelay *time.Duration - TicketEV *string - MaxFaceValue *string - MaxTicketEV *string - MaxTotalEV *string - DepositMultiplier *int - PricePerUnit *string - PixelsPerUnit *string - PriceFeedAddr *string - AutoAdjustPrice *bool - PricePerGateway *string - PricePerBroadcaster *string - BlockPollingInterval *int - Redeemer *bool - RedeemerAddr *string - Reward *bool - Monitor *bool - MetricsPerStream *bool - MetricsExposeClientIP *bool - MetadataQueueUri *string - MetadataAmqpExchange *string - MetadataPublishTimeout *time.Duration - Datadir *string - AIModelsDir *string - Objectstore *string - Recordstore *string - FVfailGsBucket *string - FVfailGsKey *string - AuthWebhookURL *string - OrchWebhookURL *string - OrchBlacklist *string - OrchMinLivepeerVersion *string - IgnorePreReleaseVersions *bool - TestOrchAvail *bool - AIRunnerImage *string + Network *string + RtmpAddr *string + CliAddr *string + HttpAddr *string + ServiceAddr *string + OrchAddr *string + VerifierURL *string + EthController *string + VerifierPath *string + LocalVerify *bool + HttpIngest *bool + Orchestrator *bool + Transcoder *bool + AIWorker *bool + Gateway *bool + Broadcaster *bool + OrchSecret *string + TranscodingOptions *string + AIModels *string + MaxAttempts *int + SelectRandWeight *float64 + SelectStakeWeight *float64 + SelectPriceWeight *float64 + SelectPriceExpFactor *float64 + OrchPerfStatsURL *string + Region *string + MaxPricePerUnit *string + MinPerfScore *float64 + MaxSessions *string + CurrentManifest *bool + Nvidia *string + Netint *string + TestTranscoder *bool + EthAcctAddr *string + EthPassword *string + EthKeystorePath *string + EthOrchAddr *string + EthUrl *string + TxTimeout *time.Duration + MaxTxReplacements *int + GasLimit *int + MinGasPrice *int64 + MaxGasPrice *int + InitializeRound *bool + InitializeRoundMaxDelay *time.Duration + TicketEV *string + MaxFaceValue *string + MaxTicketEV *string + MaxTotalEV *string + DepositMultiplier *int + PricePerUnit *string + PixelsPerUnit *string + PriceFeedAddr *string + AutoAdjustPrice *bool + PricePerGateway *string + PricePerBroadcaster *string + BlockPollingInterval *int + Redeemer *bool + RedeemerAddr *string + Reward *bool + Monitor *bool + MetricsPerStream *bool + MetricsExposeClientIP *bool + MetadataQueueUri *string + MetadataAmqpExchange *string + MetadataPublishTimeout *time.Duration + Datadir *string + AIModelsDir *string + Objectstore *string + Recordstore *string + FVfailGsBucket *string + FVfailGsKey *string + AuthWebhookURL *string + OrchWebhookURL *string + OrchBlacklist *string + OrchMinLivepeerVersion *string + TestOrchAvail *bool + AIRunnerImage *string } // DefaultLivepeerConfig creates LivepeerConfig exactly the same as when no flags are passed to the livepeer process. @@ -251,7 +250,6 @@ func DefaultLivepeerConfig() LivepeerConfig { defaultAuthWebhookURL := "" defaultOrchWebhookURL := "" defaultMinLivepeerVersion := "" - defaultIgnorePreReleaseVersions := true // Flags defaultTestOrchAvail := true @@ -348,8 +346,7 @@ func DefaultLivepeerConfig() LivepeerConfig { OrchWebhookURL: &defaultOrchWebhookURL, // Versioning constraints - OrchMinLivepeerVersion: &defaultMinLivepeerVersion, - IgnorePreReleaseVersions: &defaultIgnorePreReleaseVersions, + OrchMinLivepeerVersion: &defaultMinLivepeerVersion, // Flags TestOrchAvail: &defaultTestOrchAvail, @@ -1389,7 +1386,6 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { n.Capabilities = core.NewCapabilitiesWithConstraints(append(transcoderCaps, aiCaps...), core.MandatoryOCapabilities(), core.Constraints{}, capabilityConstraints) if cfg.OrchMinLivepeerVersion != nil { n.Capabilities.SetMinVersionConstraint(*cfg.OrchMinLivepeerVersion) - n.Capabilities.SetIgnorePreReleaseVersions(*cfg.IgnorePreReleaseVersions) } if drivers.NodeStorage == nil { diff --git a/core/capabilities.go b/core/capabilities.go index 1fd6ee2f9..95e8fdb80 100644 --- a/core/capabilities.go +++ b/core/capabilities.go @@ -21,8 +21,7 @@ type ModelConstraint struct { type Capability int type CapabilityString []uint64 type Constraints struct { - minVersion string - ignorePreReleaseVersions bool + minVersion string } type PerCapabilityConstraints struct { // Models contains a *ModelConstraint for each supported model ID @@ -406,8 +405,9 @@ func (bcast *Capabilities) LivepeerVersionCompatibleWith(orch *net.Capabilities) return false } - // By default ignore prerelease versions as in go-livepeer we actually define post-release suffixes - if bcast.constraints.ignorePreReleaseVersions { + // If minVersion has no pre-release component, ignore pre-release versions by + // default as in go-livepeer, we define post-release suffixes. + if minVer.Prerelease() == "" { minVerNoSuffix, _ := minVer.SetPrerelease("") verNoSuffix, _ := ver.SetPrerelease("") minVer = &minVerNoSuffix @@ -455,7 +455,7 @@ func (c *Capabilities) ToNetCapabilities() *net.Capabilities { } c.mutex.Lock() defer c.mutex.Unlock() - netCaps := &net.Capabilities{Bitstring: c.bitstring, Mandatories: c.mandatories, Version: c.version, Capacities: make(map[uint32]uint32), Constraints: &net.Capabilities_Constraints{MinVersion: c.constraints.minVersion, IgnorePreReleaseVersions: c.constraints.ignorePreReleaseVersions}, CapabilityConstraints: make(map[uint32]*net.Capabilities_CapabilityConstraints)} + netCaps := &net.Capabilities{Bitstring: c.bitstring, Mandatories: c.mandatories, Version: c.version, Capacities: make(map[uint32]uint32), Constraints: &net.Capabilities_Constraints{MinVersion: c.constraints.minVersion}, CapabilityConstraints: make(map[uint32]*net.Capabilities_CapabilityConstraints)} for capability, capacity := range c.capacities { netCaps.Capacities[uint32(capability)] = uint32(capacity) } @@ -483,7 +483,7 @@ func CapabilitiesFromNetCapabilities(caps *net.Capabilities) *Capabilities { mandatories: caps.Mandatories, capacities: make(map[Capability]int), version: caps.Version, - constraints: Constraints{minVersion: caps.Constraints.GetMinVersion(), ignorePreReleaseVersions: caps.Constraints.GetIgnorePreReleaseVersions()}, + constraints: Constraints{minVersion: caps.Constraints.GetMinVersion()}, capabilityConstraints: make(CapabilityConstraints), } if caps.Capacities == nil || len(caps.Capacities) == 0 { @@ -723,16 +723,3 @@ func (bcast *Capabilities) MinVersionConstraint() string { } return "" } - -func (bcast *Capabilities) SetIgnorePreReleaseVersions(ignorePreReleaseVersions bool) { - if bcast != nil { - bcast.constraints.ignorePreReleaseVersions = ignorePreReleaseVersions - } -} - -func (bcast *Capabilities) IgnorePreReleaseVersions() bool { - if bcast != nil { - return bcast.constraints.ignorePreReleaseVersions - } - return false -} diff --git a/core/capabilities_test.go b/core/capabilities_test.go index 1bc7dfb0b..64bdee1e0 100644 --- a/core/capabilities_test.go +++ b/core/capabilities_test.go @@ -346,11 +346,17 @@ func TestCapability_CompatibleWithNetCap(t *testing.T) { orch.version = "0.4.1" assert.True(bcast.CompatibleWith(orch.ToNetCapabilities())) + // broadcaster is compatible with orchestrator - no pre-release min version suffix + orch = NewCapabilities(nil, nil) + bcast = NewCapabilities(nil, nil) + bcast.constraints.minVersion = "0.4.1" + orch.version = "0.4.1-0.21000000000000-06f1f383fb18" + assert.True(bcast.CompatibleWith(orch.ToNetCapabilities())) + // broadcaster is not compatible with orchestrator pre-release - old O's version orch = NewCapabilities(nil, nil) bcast = NewCapabilities(nil, nil) bcast.constraints.minVersion = "0.4.1-0.21000000000000-06f1f383fb18" - bcast.constraints.ignorePreReleaseVersions = false orch.version = "0.4.1-0.20000000000000-06f1f383fb18" assert.False(bcast.CompatibleWith(orch.ToNetCapabilities())) @@ -358,7 +364,6 @@ func TestCapability_CompatibleWithNetCap(t *testing.T) { orch = NewCapabilities(nil, nil) bcast = NewCapabilities(nil, nil) bcast.constraints.minVersion = "0.4.1-0.20000000000000-06f1f383fb18" - bcast.constraints.ignorePreReleaseVersions = false orch.version = "0.4.1-0.21000000000000-06f1f383fb18" assert.True(bcast.CompatibleWith(orch.ToNetCapabilities())) diff --git a/net/lp_rpc.pb.go b/net/lp_rpc.pb.go index 772e30d1e..461cbfb79 100644 --- a/net/lp_rpc.pb.go +++ b/net/lp_rpc.pb.go @@ -609,11 +609,10 @@ func (m *Capabilities) GetCapabilityConstraints() map[uint32]*Capabilities_Capab // Non-binary general constraints. type Capabilities_Constraints struct { - MinVersion string `protobuf:"bytes,1,opt,name=minVersion,proto3" json:"minVersion,omitempty"` - IgnorePreReleaseVersions bool `protobuf:"varint,2,opt,name=ignorePreReleaseVersions,proto3" json:"ignorePreReleaseVersions,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + MinVersion string `protobuf:"bytes,1,opt,name=minVersion,proto3" json:"minVersion,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Capabilities_Constraints) Reset() { *m = Capabilities_Constraints{} } @@ -648,13 +647,6 @@ func (m *Capabilities_Constraints) GetMinVersion() string { return "" } -func (m *Capabilities_Constraints) GetIgnorePreReleaseVersions() bool { - if m != nil { - return m.IgnorePreReleaseVersions - } - return false -} - // Non-binary capability constraints, such as supported ranges. type Capabilities_CapabilityConstraints struct { Models map[string]*Capabilities_CapabilityConstraints_ModelConstraint `protobuf:"bytes,1,rep,name=models,proto3" json:"models,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` @@ -1917,133 +1909,132 @@ func init() { } var fileDescriptor_034e29c79f9ba827 = []byte{ - // 2041 bytes of a gzipped FileDescriptorProto + // 2021 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x58, 0xdd, 0x72, 0xdb, 0xc6, - 0x15, 0x16, 0x7f, 0xc4, 0x9f, 0x43, 0x52, 0x82, 0xd6, 0x96, 0x0c, 0x33, 0x76, 0x2a, 0x23, 0x51, - 0xea, 0xcc, 0x34, 0x8a, 0x87, 0x92, 0x9d, 0x38, 0x33, 0x99, 0x56, 0x3f, 0xb4, 0xc4, 0xd4, 0x92, - 0x38, 0x4b, 0xd9, 0x33, 0xed, 0x45, 0x59, 0x08, 0x58, 0x92, 0xa8, 0x40, 0x00, 0x5a, 0x2c, 0x63, - 0x29, 0xd3, 0x17, 0xe8, 0x23, 0xb4, 0x37, 0x9d, 0xe9, 0x4c, 0xdf, 0xa3, 0x0f, 0xd0, 0x07, 0xe8, - 0x5b, 0xf4, 0xa6, 0xf7, 0xed, 0xec, 0xd9, 0x05, 0x08, 0x8a, 0x74, 0xa2, 0xfa, 0x6e, 0xcf, 0xef, - 0x9e, 0x3d, 0x7b, 0xf6, 0x3b, 0x07, 0x00, 0x23, 0x60, 0xe2, 0x4b, 0x3f, 0xea, 0xf3, 0xc8, 0xd9, - 0x8e, 0x78, 0x28, 0x42, 0x52, 0x08, 0x98, 0xb0, 0x36, 0xa1, 0xd2, 0xf5, 0x82, 0x61, 0x37, 0x0c, - 0x86, 0xe4, 0x3e, 0x2c, 0x7f, 0x6f, 0xfb, 0x13, 0x66, 0xe6, 0x36, 0x73, 0x4f, 0xeb, 0x54, 0x11, - 0xd6, 0x09, 0x3c, 0x6a, 0x07, 0xee, 0x39, 0xb7, 0x83, 0xd8, 0x09, 0x5d, 0x2f, 0x18, 0xf6, 0x58, - 0x1c, 0x7b, 0x61, 0x40, 0xd9, 0xd5, 0x84, 0xc5, 0x82, 0x7c, 0x01, 0x60, 0x4f, 0xc4, 0xa8, 0x2f, - 0xc2, 0x4b, 0x16, 0xa0, 0x69, 0xad, 0xb5, 0xb2, 0x1d, 0x30, 0xb1, 0xbd, 0x37, 0x11, 0xa3, 0x73, - 0xc9, 0xa5, 0x55, 0x3b, 0x59, 0x5a, 0x3f, 0x83, 0xc7, 0xef, 0x71, 0x17, 0x47, 0x61, 0x10, 0x33, - 0xeb, 0x1a, 0xee, 0x9d, 0x71, 0x67, 0xc4, 0x62, 0xc1, 0x6d, 0x11, 0xf2, 0x64, 0x1b, 0x13, 0xca, - 0xb6, 0xeb, 0x72, 0x16, 0xc7, 0x3a, 0xbc, 0x84, 0x24, 0x06, 0x14, 0x62, 0x6f, 0x68, 0xe6, 0x91, - 0x2b, 0x97, 0xe4, 0x39, 0xd4, 0x1d, 0x3b, 0xb2, 0x2f, 0x3c, 0xdf, 0x13, 0x1e, 0x8b, 0xcd, 0x02, - 0x06, 0xb5, 0x86, 0x41, 0x1d, 0x64, 0x04, 0x74, 0x46, 0xcd, 0xfa, 0x73, 0x0e, 0x4a, 0x67, 0xbd, - 0x4e, 0x30, 0x08, 0xc9, 0x4b, 0xa8, 0xc5, 0x22, 0xe4, 0xf6, 0x90, 0x9d, 0xdf, 0x44, 0x2a, 0x21, - 0x2b, 0xad, 0x07, 0xe8, 0x40, 0x69, 0x6c, 0xf7, 0xa6, 0x62, 0x9a, 0xd5, 0x25, 0x5b, 0x50, 0x8a, - 0x77, 0xbc, 0x60, 0x10, 0x9a, 0x06, 0x6e, 0xdb, 0x40, 0xab, 0xde, 0x8e, 0xb2, 0xa3, 0x5a, 0x68, - 0x7d, 0x01, 0xb5, 0x8c, 0x0b, 0x02, 0x50, 0x3a, 0xec, 0xd0, 0xf6, 0xc1, 0xb9, 0xb1, 0x44, 0x4a, - 0x90, 0xef, 0xed, 0x18, 0x39, 0xc9, 0x3b, 0x3a, 0x3b, 0x3b, 0x7a, 0xdd, 0x36, 0xf2, 0xd6, 0xdf, - 0x72, 0x50, 0x49, 0x7c, 0x10, 0x02, 0xc5, 0x51, 0x18, 0x0b, 0x0c, 0xab, 0x4a, 0x71, 0x2d, 0xb3, - 0x70, 0xc9, 0x6e, 0x30, 0x0b, 0x55, 0x2a, 0x97, 0x64, 0x03, 0x4a, 0x51, 0xe8, 0x7b, 0xce, 0x0d, - 0x9e, 0xbf, 0x4a, 0x35, 0x45, 0x1e, 0x41, 0x35, 0xf6, 0x86, 0x81, 0x2d, 0x26, 0x9c, 0x99, 0x45, - 0x14, 0x4d, 0x19, 0xe4, 0x63, 0x00, 0x87, 0x33, 0x97, 0x05, 0xc2, 0xb3, 0x7d, 0x73, 0x19, 0xc5, - 0x19, 0x0e, 0x69, 0x42, 0xe5, 0x7a, 0x6f, 0xfc, 0xc3, 0xa1, 0x2d, 0x98, 0x59, 0x42, 0x69, 0x4a, - 0x5b, 0x6f, 0xa0, 0xda, 0xe5, 0x9e, 0xc3, 0x30, 0x48, 0x0b, 0xea, 0x91, 0x24, 0xba, 0x8c, 0xbf, - 0x09, 0x3c, 0x15, 0x6c, 0x81, 0xce, 0xf0, 0xc8, 0xa7, 0xd0, 0x88, 0xbc, 0x6b, 0xe6, 0xc7, 0x89, - 0x52, 0x1e, 0x95, 0x66, 0x99, 0xd6, 0xbf, 0x4b, 0x50, 0xcf, 0x5e, 0x9b, 0x3c, 0xc1, 0x85, 0x27, - 0x62, 0xc1, 0xbd, 0x60, 0x68, 0xe6, 0x36, 0x0b, 0x4f, 0x8b, 0x74, 0xca, 0x20, 0x9b, 0x50, 0x1b, - 0xdb, 0x81, 0x2b, 0x8b, 0x47, 0x5e, 0x7e, 0x1e, 0xe5, 0x59, 0x16, 0xd9, 0x03, 0x90, 0x17, 0xef, - 0x24, 0xd5, 0x51, 0x78, 0x5a, 0x6b, 0x3d, 0x99, 0xab, 0x0e, 0x24, 0x94, 0x4e, 0x3b, 0x10, 0xfc, - 0x86, 0x66, 0x8c, 0x64, 0x39, 0x7e, 0xcf, 0xb8, 0x2c, 0x5c, 0x9d, 0xc2, 0x84, 0x24, 0xbf, 0x84, - 0x9a, 0x13, 0x06, 0xb2, 0x7a, 0xbd, 0x40, 0xc4, 0x98, 0xc1, 0x5a, 0xeb, 0xf1, 0x02, 0xef, 0x53, - 0x25, 0x9a, 0xb5, 0x20, 0x17, 0xb0, 0x9e, 0x96, 0xe5, 0x4d, 0x46, 0xcb, 0x2c, 0x61, 0xa0, 0xbf, - 0x58, 0x1c, 0xe8, 0x9c, 0xba, 0x8a, 0x79, 0xb1, 0xab, 0xe6, 0xb7, 0xb0, 0x7a, 0xeb, 0x74, 0x49, - 0x01, 0xc9, 0x6b, 0x6a, 0xa8, 0x02, 0x4a, 0xf1, 0x20, 0x8f, 0x3c, 0x45, 0x7c, 0x93, 0xff, 0x3a, - 0xd7, 0xf4, 0xa0, 0x96, 0xf1, 0x26, 0x6b, 0x66, 0xec, 0x05, 0x6f, 0x75, 0x3e, 0x54, 0x55, 0x66, - 0x38, 0xe4, 0x1b, 0x30, 0xbd, 0x61, 0x10, 0x72, 0xd6, 0xe5, 0x8c, 0x32, 0x9f, 0xd9, 0x31, 0xd3, - 0xa2, 0x18, 0x7d, 0x57, 0xe8, 0x7b, 0xe5, 0xcd, 0xff, 0xe6, 0x60, 0x7d, 0xe1, 0xf9, 0xc8, 0xaf, - 0xa1, 0x34, 0x0e, 0x5d, 0xe6, 0xc7, 0x58, 0x02, 0xb5, 0xd6, 0xce, 0x1d, 0x13, 0xb3, 0x7d, 0x82, - 0x56, 0x2a, 0x3f, 0xda, 0x45, 0x73, 0x0b, 0x56, 0x91, 0x3d, 0xd5, 0x93, 0xaf, 0xec, 0x9d, 0xcd, - 0xc7, 0x78, 0x9e, 0x0a, 0xc5, 0x75, 0x93, 0x43, 0x2d, 0x63, 0x9d, 0xcd, 0x99, 0x7e, 0x74, 0x27, - 0xd9, 0x9c, 0xd5, 0x5a, 0x5f, 0xfd, 0x5f, 0x31, 0x4d, 0x19, 0xd9, 0x64, 0x5f, 0x41, 0xf3, 0xfd, - 0x17, 0xbc, 0xe0, 0xda, 0xbe, 0x9d, 0x0d, 0xe1, 0xe7, 0x77, 0x0c, 0x21, 0xb3, 0xa5, 0xf5, 0x8f, - 0x3c, 0x18, 0x59, 0x10, 0xc6, 0x07, 0xfd, 0x31, 0x80, 0xd0, 0xb0, 0xcd, 0x78, 0x72, 0xcb, 0x53, - 0x0e, 0x79, 0x01, 0x0d, 0xe1, 0x39, 0x97, 0x4c, 0xf4, 0x23, 0x9b, 0xdb, 0xe3, 0x58, 0xef, 0xaf, - 0x60, 0xf7, 0x1c, 0x25, 0x5d, 0x14, 0xd0, 0xba, 0xc8, 0x50, 0xb2, 0x81, 0x20, 0x28, 0xf4, 0x11, - 0x34, 0x0b, 0x99, 0x06, 0x92, 0x82, 0x09, 0xad, 0x46, 0x29, 0xae, 0x64, 0x1a, 0x41, 0x71, 0xb6, - 0x11, 0xdc, 0x86, 0xfd, 0xe5, 0x3b, 0xc1, 0xfe, 0xad, 0x06, 0x56, 0xfa, 0x89, 0x06, 0x46, 0xb6, - 0xa0, 0xac, 0xe1, 0xde, 0xdc, 0xc4, 0xba, 0xab, 0x65, 0xda, 0x02, 0x4d, 0x64, 0xd6, 0xef, 0xa1, - 0x9a, 0x9a, 0xcb, 0x97, 0x34, 0x6d, 0x8f, 0x75, 0xaa, 0x08, 0xf2, 0x18, 0x20, 0x56, 0xcd, 0xaf, - 0xef, 0xb9, 0x1a, 0xb9, 0xab, 0x9a, 0xd3, 0x71, 0x65, 0xbe, 0xd9, 0x75, 0xe4, 0x71, 0x5b, 0xc8, - 0x57, 0x55, 0x40, 0x64, 0xcc, 0x70, 0xac, 0xff, 0x14, 0xa1, 0xdc, 0x63, 0xc3, 0x43, 0x5b, 0xd8, - 0xf8, 0x02, 0xed, 0xc0, 0x1b, 0xb0, 0x58, 0x74, 0x5c, 0xbd, 0x4b, 0x86, 0x83, 0x3d, 0x92, 0x5d, - 0x69, 0x78, 0x95, 0x4b, 0xec, 0x21, 0x76, 0x3c, 0x42, 0xbf, 0x75, 0x8a, 0x6b, 0x89, 0xed, 0x11, - 0x0f, 0x07, 0x9e, 0xcf, 0x92, 0xdc, 0xa6, 0x74, 0xd2, 0x65, 0x97, 0xa7, 0x5d, 0xb6, 0x09, 0x15, - 0x77, 0xa2, 0xa3, 0x93, 0x59, 0x5b, 0xa6, 0x29, 0x3d, 0x77, 0x15, 0xe5, 0x0f, 0xb9, 0x8a, 0xca, - 0x4f, 0x5d, 0xc5, 0x33, 0xb8, 0xef, 0xd8, 0xbe, 0xd3, 0x8f, 0x18, 0x77, 0x58, 0x24, 0x26, 0xb6, - 0xdf, 0xc7, 0x33, 0x01, 0xbe, 0x58, 0x22, 0x65, 0xdd, 0x54, 0x74, 0x2c, 0x4f, 0x78, 0xb7, 0xcb, - 0x93, 0xe1, 0x0f, 0x26, 0xbe, 0xdf, 0x4d, 0x92, 0xf1, 0x04, 0x75, 0x55, 0xf8, 0x6f, 0x3d, 0x97, - 0x85, 0x5a, 0x42, 0x67, 0xd4, 0xc8, 0x57, 0xd0, 0xc8, 0xd2, 0x2d, 0xd3, 0x7a, 0x9f, 0xdd, 0xac, - 0xde, 0x6d, 0xc3, 0x1d, 0xf3, 0x93, 0x3b, 0x19, 0xee, 0x90, 0x3d, 0x20, 0x31, 0x1b, 0x8e, 0x59, - 0xa0, 0x1f, 0x1d, 0x13, 0x8c, 0xc7, 0xe6, 0x16, 0x26, 0x8e, 0xa8, 0xc1, 0x83, 0x0d, 0xbb, 0xa9, - 0x84, 0xae, 0x69, 0xed, 0x29, 0x8b, 0x6c, 0x03, 0x79, 0x15, 0x72, 0x87, 0xa5, 0x73, 0x98, 0x27, - 0x1b, 0xf1, 0x67, 0x2a, 0x85, 0xf3, 0x12, 0x6b, 0x07, 0x1a, 0x33, 0x3e, 0x65, 0x25, 0x0d, 0x78, - 0xa8, 0x70, 0xb2, 0x48, 0x71, 0x4d, 0x56, 0x20, 0x2f, 0x42, 0x2c, 0xb7, 0x22, 0xcd, 0x8b, 0xd0, - 0xfa, 0xe7, 0x32, 0xd4, 0xb3, 0xe7, 0x90, 0x46, 0x81, 0x3d, 0x66, 0x38, 0x23, 0x55, 0x29, 0xae, - 0xe5, 0x2b, 0x79, 0xe7, 0xb9, 0x62, 0x64, 0xae, 0x61, 0x35, 0x29, 0x42, 0x8e, 0x31, 0x23, 0xe6, - 0x0d, 0x47, 0xc2, 0x24, 0xc8, 0xd6, 0x94, 0xc4, 0x81, 0x0b, 0x4f, 0xc2, 0x13, 0x33, 0xef, 0xa1, - 0x20, 0x21, 0x65, 0xa9, 0x0e, 0xa2, 0xd8, 0xbc, 0xaf, 0x20, 0x71, 0x10, 0xc5, 0xe4, 0x19, 0x94, - 0x06, 0x21, 0x1f, 0xdb, 0xc2, 0x5c, 0xc7, 0x49, 0xce, 0x9c, 0x4b, 0xec, 0xf6, 0x2b, 0x94, 0x53, - 0xad, 0x27, 0x77, 0x1d, 0x44, 0xf1, 0x21, 0x0b, 0xcc, 0x0d, 0x74, 0xa3, 0x29, 0xb2, 0x03, 0x65, - 0xfd, 0x24, 0xcc, 0x07, 0xe8, 0xea, 0xe1, 0xbc, 0xab, 0xe4, 0xae, 0x12, 0x4d, 0x19, 0xd0, 0x30, - 0x8c, 0x4c, 0x13, 0xc3, 0x94, 0x4b, 0xf2, 0x02, 0xca, 0x2c, 0x50, 0x40, 0xfa, 0x10, 0xdd, 0x3c, - 0x9a, 0x77, 0x83, 0xc4, 0x41, 0xe8, 0x32, 0x87, 0x26, 0xca, 0x38, 0x9d, 0x85, 0x7e, 0xc8, 0x0f, - 0x59, 0x24, 0x46, 0x66, 0x13, 0x1d, 0x66, 0x38, 0xe4, 0x08, 0xea, 0xce, 0x88, 0x87, 0x63, 0x5b, - 0x1d, 0xc7, 0xfc, 0x08, 0x9d, 0x7f, 0x32, 0xef, 0xfc, 0x00, 0xb5, 0x7a, 0x93, 0x8b, 0xd8, 0x1e, - 0x47, 0xbe, 0x17, 0x0c, 0xe9, 0x8c, 0xa1, 0xcc, 0xee, 0xd5, 0xc4, 0x96, 0x2d, 0xc2, 0x7c, 0x84, - 0x09, 0x48, 0x48, 0xeb, 0x31, 0x94, 0xb4, 0x0e, 0x40, 0xe9, 0xa4, 0xdb, 0x3e, 0x3a, 0xef, 0x19, - 0x4b, 0xa4, 0x0c, 0x85, 0x93, 0xee, 0xae, 0x91, 0xb3, 0xfe, 0x00, 0xe5, 0xe4, 0x8e, 0xef, 0xc1, - 0x6a, 0xfb, 0xf4, 0xe0, 0xec, 0xb0, 0x4d, 0xfb, 0x87, 0xed, 0x57, 0x7b, 0x6f, 0x5e, 0xcb, 0xe1, - 0x76, 0x0d, 0x1a, 0xc7, 0xad, 0x17, 0xbb, 0xfd, 0xfd, 0xbd, 0x5e, 0xfb, 0x75, 0xe7, 0xb4, 0x6d, - 0xe4, 0x48, 0x03, 0xaa, 0xc8, 0x3a, 0xd9, 0xeb, 0x9c, 0x1a, 0xf9, 0x94, 0x3c, 0xee, 0x1c, 0x1d, - 0x1b, 0x05, 0xf2, 0x10, 0xd6, 0x91, 0x3c, 0x38, 0x3b, 0xed, 0x9d, 0xd3, 0xbd, 0xce, 0x69, 0xfb, - 0x50, 0x89, 0x8a, 0x56, 0x0b, 0x60, 0x9a, 0x24, 0x52, 0x81, 0xa2, 0x54, 0x34, 0x96, 0xf4, 0xea, - 0xb9, 0x91, 0x93, 0x61, 0xbd, 0xed, 0x7e, 0x6d, 0xe4, 0xd5, 0xe2, 0xa5, 0x51, 0xb0, 0x0e, 0x60, - 0x6d, 0xee, 0xec, 0x64, 0x05, 0xe0, 0xe0, 0x98, 0x9e, 0x9d, 0xec, 0xf5, 0x77, 0x5b, 0xcf, 0x8c, - 0xa5, 0x19, 0xba, 0x65, 0xe4, 0xb2, 0xf4, 0xee, 0xae, 0x91, 0xb7, 0xae, 0x60, 0x3d, 0xf9, 0x82, - 0x61, 0x6e, 0x4f, 0x3d, 0x29, 0xc4, 0x61, 0x03, 0x0a, 0x13, 0xee, 0x27, 0x03, 0xc1, 0x84, 0xfb, - 0x38, 0x85, 0xe3, 0x34, 0xab, 0xc1, 0x57, 0x53, 0x64, 0x1b, 0xee, 0xdd, 0x82, 0xad, 0xbe, 0xb4, - 0x54, 0xa3, 0xfa, 0x5a, 0x34, 0x03, 0x5b, 0x6f, 0xb8, 0x6f, 0xfd, 0x06, 0x1a, 0xe9, 0x96, 0xb8, - 0xd5, 0x0b, 0xa8, 0xe8, 0xc7, 0x9c, 0x0c, 0x40, 0x4d, 0xd5, 0x69, 0x17, 0x05, 0x46, 0x53, 0xdd, - 0xf9, 0xcf, 0x25, 0xeb, 0x2f, 0x39, 0x58, 0x4d, 0xad, 0x28, 0x8b, 0x27, 0xbe, 0x48, 0x1a, 0x46, - 0x6e, 0xda, 0x30, 0x36, 0x60, 0x99, 0x71, 0x1e, 0x72, 0xd5, 0xa8, 0x8e, 0x97, 0xa8, 0x22, 0xc9, - 0x53, 0x28, 0xba, 0xb6, 0xb0, 0x75, 0xe3, 0x26, 0xb3, 0x31, 0xc8, 0xbd, 0x8f, 0x97, 0x28, 0x6a, - 0x90, 0xcf, 0xa1, 0x98, 0xf9, 0x2e, 0x5a, 0x57, 0xc8, 0x7b, 0x6b, 0xca, 0xa0, 0xa8, 0xb2, 0x5f, - 0x81, 0x12, 0xc7, 0x40, 0xac, 0x3f, 0xc2, 0x2a, 0x65, 0x43, 0x2f, 0x16, 0x2c, 0xfd, 0x14, 0xdc, - 0x80, 0x52, 0xcc, 0x1c, 0xce, 0x92, 0x0f, 0x20, 0x4d, 0xc9, 0x86, 0xa4, 0x27, 0xf4, 0x1b, 0x9d, - 0xec, 0x94, 0xfe, 0xd0, 0x4f, 0xc2, 0x3f, 0xe5, 0xa0, 0x71, 0x1a, 0x0a, 0x6f, 0x70, 0xa3, 0x93, - 0xb9, 0xe0, 0x86, 0x3f, 0x83, 0x72, 0xac, 0xda, 0xb0, 0xf6, 0x5a, 0x4f, 0x80, 0x17, 0x33, 0x9f, - 0x08, 0x65, 0xd8, 0xc2, 0x8e, 0x2f, 0x3b, 0x2e, 0x26, 0xa0, 0x40, 0x35, 0x35, 0xd3, 0x75, 0xd7, - 0x66, 0xbb, 0xee, 0x77, 0xc5, 0x4a, 0xde, 0x28, 0x7c, 0x57, 0xac, 0x3c, 0x31, 0x2c, 0xeb, 0xaf, - 0x79, 0xa8, 0x67, 0xc7, 0x28, 0xf9, 0x19, 0xc4, 0x99, 0xe3, 0x45, 0x1e, 0x0b, 0x84, 0xee, 0xf9, - 0x53, 0x86, 0x9c, 0x2e, 0x06, 0xb6, 0xc3, 0xfa, 0xd3, 0x59, 0xb0, 0x4e, 0xab, 0x92, 0xf3, 0x56, - 0x32, 0xc8, 0x43, 0xa8, 0xbc, 0xf3, 0x82, 0x7e, 0xc4, 0xc3, 0x0b, 0x3d, 0x03, 0x94, 0xdf, 0x79, - 0x41, 0x97, 0x87, 0x17, 0xb2, 0x34, 0x53, 0x37, 0x7d, 0x6e, 0x07, 0xae, 0xea, 0xaa, 0x6a, 0x22, - 0x58, 0x4b, 0x45, 0xd4, 0x0e, 0x5c, 0x6c, 0xaa, 0x04, 0x8a, 0x31, 0x63, 0xae, 0x9e, 0x0d, 0x70, - 0x4d, 0x3e, 0x07, 0x63, 0x3a, 0xaa, 0xf4, 0x2f, 0xfc, 0xd0, 0xb9, 0xc4, 0x21, 0xa1, 0x4e, 0x57, - 0xa7, 0xfc, 0x7d, 0xc9, 0x26, 0xc7, 0xb0, 0x96, 0x51, 0xd5, 0xb3, 0xa3, 0x1a, 0x18, 0x3e, 0xca, - 0xcc, 0x8e, 0xed, 0x54, 0x47, 0x4f, 0x91, 0x99, 0x0d, 0x14, 0xc7, 0xea, 0x00, 0x51, 0xba, 0x3d, - 0x16, 0xb8, 0x8c, 0xeb, 0x34, 0x3d, 0x81, 0x7a, 0x8c, 0x74, 0x3f, 0x08, 0x03, 0x87, 0xe9, 0x51, - 0xb9, 0xa6, 0x78, 0xa7, 0x92, 0xb5, 0xe0, 0x4d, 0xfc, 0x00, 0x1b, 0x8b, 0xb7, 0x25, 0x5b, 0xb0, - 0xe2, 0x70, 0xa6, 0x82, 0xe5, 0xe1, 0x24, 0x70, 0xf5, 0x23, 0x69, 0x24, 0x5c, 0x2a, 0x99, 0xe4, - 0x25, 0x3c, 0x9c, 0x55, 0x53, 0x49, 0x50, 0xa9, 0x54, 0x1b, 0x6d, 0xcc, 0x58, 0x60, 0x32, 0x64, - 0x3e, 0xad, 0xbf, 0xe7, 0xa1, 0xdc, 0xb5, 0x6f, 0xb0, 0xdc, 0xe6, 0x86, 0xea, 0xdc, 0xdd, 0x86, - 0x6a, 0x7c, 0x23, 0xf2, 0x80, 0x7a, 0x2f, 0x4d, 0x2d, 0x4e, 0x76, 0xe1, 0x03, 0x92, 0x4d, 0x3a, - 0x70, 0x5f, 0x47, 0xa6, 0xb3, 0xab, 0x9d, 0x15, 0x11, 0x8b, 0x1e, 0x64, 0x9c, 0x65, 0x6f, 0x83, - 0x12, 0x31, 0x7f, 0x43, 0xcf, 0x61, 0x85, 0x5d, 0x47, 0xcc, 0x11, 0xcc, 0xed, 0xe3, 0xa0, 0xaf, - 0x47, 0xf7, 0xdb, 0x5f, 0x01, 0x8d, 0x44, 0x0b, 0x59, 0xad, 0x7f, 0xe5, 0xa0, 0x9e, 0xc5, 0x0f, - 0xb2, 0x0f, 0xab, 0x47, 0x4c, 0xcc, 0xb0, 0xcc, 0x39, 0x94, 0xd1, 0x28, 0xd2, 0x5c, 0x8c, 0x3f, - 0xe4, 0x77, 0xb0, 0xbe, 0xf0, 0xff, 0x14, 0x51, 0x3f, 0x08, 0x7e, 0xec, 0x57, 0x58, 0xd3, 0xfa, - 0x31, 0x15, 0xf5, 0x7b, 0x8b, 0x7c, 0x0a, 0xc5, 0xae, 0x6c, 0x39, 0xea, 0xb7, 0x50, 0xf2, 0xef, - 0xad, 0x39, 0x4b, 0xb6, 0x4e, 0x01, 0xce, 0xa7, 0x5f, 0x56, 0xbf, 0x02, 0x92, 0x60, 0x60, 0x86, - 0x7b, 0x1f, 0x4d, 0x6e, 0x81, 0x63, 0x53, 0x01, 0xf0, 0x0c, 0x66, 0x3d, 0xcb, 0xed, 0x97, 0x7f, - 0xbb, 0xbc, 0xfd, 0x65, 0xc0, 0xc4, 0x45, 0x09, 0xff, 0xfd, 0xed, 0xfc, 0x2f, 0x00, 0x00, 0xff, - 0xff, 0x90, 0xe3, 0x7f, 0x0a, 0x0f, 0x14, 0x00, 0x00, + 0xf5, 0x17, 0x3f, 0xc4, 0x8f, 0x43, 0x52, 0x82, 0xd6, 0x96, 0x0c, 0x33, 0x76, 0xfe, 0x32, 0x12, + 0xe5, 0xef, 0xcc, 0xd4, 0x8a, 0x87, 0x92, 0x9d, 0xb8, 0x33, 0x99, 0x56, 0x1f, 0xb4, 0xc4, 0xd4, + 0x92, 0x38, 0x4b, 0xd9, 0x33, 0xed, 0x45, 0x59, 0x08, 0x58, 0x92, 0xa8, 0x48, 0x00, 0x5a, 0x2c, + 0x63, 0x29, 0xd3, 0x17, 0xe8, 0x23, 0xb4, 0x37, 0x9d, 0xe9, 0x4c, 0x9f, 0xa0, 0x2f, 0xd0, 0x07, + 0xe8, 0x03, 0xf4, 0x41, 0x7a, 0xdf, 0xce, 0x9e, 0x5d, 0x80, 0x80, 0x48, 0x27, 0xaa, 0xef, 0xf6, + 0x7c, 0xee, 0xd9, 0xb3, 0x67, 0x7f, 0xe7, 0x00, 0x60, 0xf8, 0x4c, 0x7c, 0x35, 0x0e, 0xfb, 0x3c, + 0x74, 0xb6, 0x43, 0x1e, 0x88, 0x80, 0x14, 0x7c, 0x26, 0xac, 0x4d, 0xa8, 0x74, 0x3d, 0x7f, 0xd8, + 0x0d, 0xfc, 0x21, 0xb9, 0x0f, 0xcb, 0xdf, 0xdb, 0xe3, 0x29, 0x33, 0x73, 0x9b, 0xb9, 0xa7, 0x75, + 0xaa, 0x08, 0xeb, 0x04, 0x1e, 0xb5, 0x7d, 0xf7, 0x9c, 0xdb, 0x7e, 0xe4, 0x04, 0xae, 0xe7, 0x0f, + 0x7b, 0x2c, 0x8a, 0xbc, 0xc0, 0xa7, 0xec, 0x6a, 0xca, 0x22, 0x41, 0x9e, 0x01, 0xd8, 0x53, 0x31, + 0xea, 0x8b, 0xe0, 0x92, 0xf9, 0x68, 0x5a, 0x6b, 0xad, 0x6c, 0xfb, 0x4c, 0x6c, 0xef, 0x4d, 0xc5, + 0xe8, 0x5c, 0x72, 0x69, 0xd5, 0x8e, 0x97, 0xd6, 0xff, 0xc1, 0xe3, 0x0f, 0xb8, 0x8b, 0xc2, 0xc0, + 0x8f, 0x98, 0x75, 0x0d, 0xf7, 0xce, 0xb8, 0x33, 0x62, 0x91, 0xe0, 0xb6, 0x08, 0x78, 0xbc, 0x8d, + 0x09, 0x65, 0xdb, 0x75, 0x39, 0x8b, 0x22, 0x1d, 0x5e, 0x4c, 0x12, 0x03, 0x0a, 0x91, 0x37, 0x34, + 0xf3, 0xc8, 0x95, 0x4b, 0xf2, 0x02, 0xea, 0x8e, 0x1d, 0xda, 0x17, 0xde, 0xd8, 0x13, 0x1e, 0x8b, + 0xcc, 0x02, 0x06, 0xb5, 0x86, 0x41, 0x1d, 0xa4, 0x04, 0x34, 0xa3, 0x66, 0xfd, 0x29, 0x07, 0xa5, + 0xb3, 0x5e, 0xc7, 0x1f, 0x04, 0xe4, 0x15, 0xd4, 0x22, 0x11, 0x70, 0x7b, 0xc8, 0xce, 0x6f, 0x42, + 0x95, 0x90, 0x95, 0xd6, 0x03, 0x74, 0xa0, 0x34, 0xb6, 0x7b, 0x33, 0x31, 0x4d, 0xeb, 0x92, 0x2d, + 0x28, 0x45, 0x3b, 0x9e, 0x3f, 0x08, 0x4c, 0x03, 0xb7, 0x6d, 0xa0, 0x55, 0x6f, 0x47, 0xd9, 0x51, + 0x2d, 0xb4, 0x9e, 0x41, 0x2d, 0xe5, 0x82, 0x00, 0x94, 0x0e, 0x3b, 0xb4, 0x7d, 0x70, 0x6e, 0x2c, + 0x91, 0x12, 0xe4, 0x7b, 0x3b, 0x46, 0x4e, 0xf2, 0x8e, 0xce, 0xce, 0x8e, 0xde, 0xb4, 0x8d, 0xbc, + 0xf5, 0xd7, 0x1c, 0x54, 0x62, 0x1f, 0x84, 0x40, 0x71, 0x14, 0x44, 0x02, 0xc3, 0xaa, 0x52, 0x5c, + 0xcb, 0x2c, 0x5c, 0xb2, 0x1b, 0xcc, 0x42, 0x95, 0xca, 0x25, 0xd9, 0x80, 0x52, 0x18, 0x8c, 0x3d, + 0xe7, 0x06, 0xcf, 0x5f, 0xa5, 0x9a, 0x22, 0x8f, 0xa0, 0x1a, 0x79, 0x43, 0xdf, 0x16, 0x53, 0xce, + 0xcc, 0x22, 0x8a, 0x66, 0x0c, 0xf2, 0x29, 0x80, 0xc3, 0x99, 0xcb, 0x7c, 0xe1, 0xd9, 0x63, 0x73, + 0x19, 0xc5, 0x29, 0x0e, 0x69, 0x42, 0xe5, 0x7a, 0x6f, 0xf2, 0xc3, 0xa1, 0x2d, 0x98, 0x59, 0x42, + 0x69, 0x42, 0x5b, 0x6f, 0xa1, 0xda, 0xe5, 0x9e, 0xc3, 0x30, 0x48, 0x0b, 0xea, 0xa1, 0x24, 0xba, + 0x8c, 0xbf, 0xf5, 0x3d, 0x15, 0x6c, 0x81, 0x66, 0x78, 0xe4, 0x73, 0x68, 0x84, 0xde, 0x35, 0x1b, + 0x47, 0xb1, 0x52, 0x1e, 0x95, 0xb2, 0x4c, 0xeb, 0xef, 0x25, 0xa8, 0xa7, 0xaf, 0x4d, 0x9e, 0xe0, + 0xc2, 0x13, 0x91, 0xe0, 0x9e, 0x3f, 0x34, 0x73, 0x9b, 0x85, 0xa7, 0x45, 0x3a, 0x63, 0x90, 0x4d, + 0xa8, 0x4d, 0x6c, 0xdf, 0x95, 0xc5, 0x23, 0x2f, 0x3f, 0x8f, 0xf2, 0x34, 0x8b, 0xec, 0x01, 0xc8, + 0x8b, 0x77, 0xe2, 0xea, 0x28, 0x3c, 0xad, 0xb5, 0x9e, 0xcc, 0x55, 0x07, 0x12, 0x4a, 0xa7, 0xed, + 0x0b, 0x7e, 0x43, 0x53, 0x46, 0xb2, 0x1c, 0xbf, 0x67, 0x5c, 0x16, 0xae, 0x4e, 0x61, 0x4c, 0x92, + 0x5f, 0x40, 0xcd, 0x09, 0x7c, 0x59, 0xbd, 0x9e, 0x2f, 0x22, 0xcc, 0x60, 0xad, 0xf5, 0x78, 0x81, + 0xf7, 0x99, 0x12, 0x4d, 0x5b, 0x90, 0x0b, 0x58, 0x4f, 0xca, 0xf2, 0x26, 0xa5, 0x65, 0x96, 0x30, + 0xd0, 0x9f, 0x2d, 0x0e, 0x74, 0x4e, 0x5d, 0xc5, 0xbc, 0xd8, 0x55, 0xf3, 0x5b, 0x58, 0xbd, 0x75, + 0xba, 0xb8, 0x80, 0xe4, 0x35, 0x35, 0x54, 0x01, 0x25, 0x78, 0x90, 0x47, 0x9e, 0x22, 0x7e, 0x9e, + 0xff, 0x26, 0xd7, 0x7c, 0x06, 0xb5, 0x94, 0x37, 0x59, 0x33, 0x13, 0xcf, 0x7f, 0xa7, 0xf3, 0xa1, + 0xaa, 0x32, 0xc5, 0x69, 0xfe, 0x27, 0x07, 0xeb, 0x0b, 0x63, 0x24, 0xbf, 0x82, 0xd2, 0x24, 0x70, + 0xd9, 0x38, 0xc2, 0x6b, 0xac, 0xb5, 0x76, 0xee, 0x78, 0xb8, 0xed, 0x13, 0xb4, 0x52, 0x67, 0xd4, + 0x2e, 0x9a, 0x5b, 0xb0, 0x8a, 0xec, 0x99, 0x9e, 0x7c, 0x29, 0xef, 0x6d, 0x3e, 0xc1, 0x98, 0x2a, + 0x14, 0xd7, 0x4d, 0x0e, 0xb5, 0x94, 0x75, 0xfa, 0xdc, 0xfa, 0xe1, 0x9c, 0xa4, 0xcf, 0x5d, 0x6b, + 0x7d, 0xfd, 0x3f, 0xc5, 0x34, 0x63, 0xa4, 0x13, 0x76, 0x05, 0xcd, 0x0f, 0x5f, 0xd2, 0x82, 0xd4, + 0x7f, 0x9b, 0x0d, 0xe1, 0xff, 0xef, 0x18, 0x42, 0x6a, 0x4b, 0xeb, 0x1f, 0x79, 0x30, 0xd2, 0x40, + 0x8a, 0x8f, 0xf2, 0x53, 0x00, 0xa1, 0xa1, 0x97, 0xf1, 0xf8, 0xa6, 0x66, 0x1c, 0xf2, 0x12, 0x1a, + 0xc2, 0x73, 0x2e, 0x99, 0xe8, 0x87, 0x36, 0xb7, 0x27, 0x91, 0xde, 0x5f, 0x41, 0xe7, 0x39, 0x4a, + 0xba, 0x28, 0xa0, 0x75, 0x91, 0xa2, 0x64, 0x13, 0xc0, 0x87, 0xdd, 0x47, 0xe0, 0x2b, 0xa4, 0x9a, + 0x40, 0x02, 0x08, 0xb4, 0x1a, 0x26, 0xd8, 0x90, 0x02, 0xf3, 0x62, 0x16, 0xcc, 0x6f, 0x43, 0xf7, + 0xf2, 0x9d, 0xa0, 0xfb, 0x56, 0x13, 0x2a, 0xfd, 0x44, 0x13, 0x22, 0x5b, 0x50, 0xd6, 0x90, 0x6d, + 0x6e, 0x62, 0xdd, 0xd5, 0x52, 0xd0, 0x4e, 0x63, 0x99, 0xf5, 0x3b, 0xa8, 0x26, 0xe6, 0xf2, 0x35, + 0xcc, 0x5a, 0x5c, 0x9d, 0x2a, 0x82, 0x3c, 0x06, 0x88, 0x54, 0x03, 0xeb, 0x7b, 0xae, 0x46, 0xdf, + 0xaa, 0xe6, 0x74, 0x5c, 0x99, 0x6f, 0x76, 0x1d, 0x7a, 0xdc, 0x16, 0xf2, 0x65, 0x14, 0x10, 0xdd, + 0x52, 0x1c, 0xeb, 0xdf, 0x45, 0x28, 0xf7, 0xd8, 0xf0, 0xd0, 0x16, 0x36, 0xbe, 0x22, 0xdb, 0xf7, + 0x06, 0x2c, 0x12, 0x1d, 0x57, 0xef, 0x92, 0xe2, 0x60, 0x9f, 0x63, 0x57, 0x1a, 0x22, 0xe5, 0x12, + 0xfb, 0x80, 0x1d, 0x8d, 0xd0, 0x6f, 0x9d, 0xe2, 0x5a, 0xe2, 0x73, 0xc8, 0x83, 0x81, 0x37, 0x66, + 0x71, 0x6e, 0x13, 0x3a, 0xee, 0x94, 0xcb, 0xb3, 0x4e, 0xd9, 0x84, 0x8a, 0x3b, 0xd5, 0xd1, 0xc9, + 0xac, 0x2d, 0xd3, 0x84, 0x9e, 0xbb, 0x8a, 0xf2, 0xc7, 0x5c, 0x45, 0xe5, 0xa7, 0xae, 0xe2, 0x39, + 0xdc, 0x77, 0xec, 0xb1, 0xd3, 0x0f, 0x19, 0x77, 0x58, 0x28, 0xa6, 0xf6, 0xb8, 0x8f, 0x67, 0x02, + 0x7c, 0xb1, 0x44, 0xca, 0xba, 0x89, 0xe8, 0x58, 0x9e, 0xf0, 0x6e, 0x97, 0x27, 0xc3, 0x1f, 0x4c, + 0xc7, 0xe3, 0x6e, 0x9c, 0x8c, 0x27, 0xa8, 0xab, 0xc2, 0x7f, 0xe7, 0xb9, 0x2c, 0xd0, 0x12, 0x9a, + 0x51, 0x23, 0x5f, 0x43, 0x23, 0x4d, 0xb7, 0x4c, 0xeb, 0x43, 0x76, 0x59, 0xbd, 0xdb, 0x86, 0x3b, + 0xe6, 0x67, 0x77, 0x32, 0xdc, 0x21, 0x7b, 0x40, 0x22, 0x36, 0x9c, 0x30, 0x5f, 0x3f, 0x3a, 0x26, + 0x18, 0x8f, 0xcc, 0x2d, 0x4c, 0x1c, 0x51, 0xc3, 0x03, 0x1b, 0x76, 0x13, 0x09, 0x5d, 0xd3, 0xda, + 0x33, 0x16, 0xd9, 0x06, 0xf2, 0x3a, 0xe0, 0x0e, 0x4b, 0x66, 0x29, 0x4f, 0x36, 0xd3, 0x2f, 0x54, + 0x0a, 0xe7, 0x25, 0xd6, 0x0e, 0x34, 0x32, 0x3e, 0x65, 0x25, 0x0d, 0x78, 0xa0, 0x70, 0xb2, 0x48, + 0x71, 0x4d, 0x56, 0x20, 0x2f, 0x02, 0x2c, 0xb7, 0x22, 0xcd, 0x8b, 0xc0, 0xfa, 0xe7, 0x32, 0xd4, + 0xd3, 0xe7, 0x90, 0x46, 0xbe, 0x3d, 0x61, 0x38, 0xe7, 0x54, 0x29, 0xae, 0xe5, 0x2b, 0x79, 0xef, + 0xb9, 0x62, 0x64, 0xae, 0x61, 0x35, 0x29, 0x42, 0x8e, 0x22, 0x23, 0xe6, 0x0d, 0x47, 0xc2, 0x24, + 0xc8, 0xd6, 0x94, 0xc4, 0x81, 0x0b, 0x4f, 0xc2, 0x13, 0x33, 0xef, 0xa1, 0x20, 0x26, 0x65, 0xa9, + 0x0e, 0xc2, 0xc8, 0xbc, 0xaf, 0x20, 0x71, 0x10, 0x46, 0xe4, 0x39, 0x94, 0x06, 0x01, 0x9f, 0xd8, + 0xc2, 0x5c, 0xc7, 0x69, 0xcc, 0x9c, 0x4b, 0xec, 0xf6, 0x6b, 0x94, 0x53, 0xad, 0x27, 0x77, 0x1d, + 0x84, 0xd1, 0x21, 0xf3, 0xcd, 0x0d, 0x74, 0xa3, 0x29, 0xb2, 0x03, 0x65, 0xfd, 0x24, 0xcc, 0x07, + 0xe8, 0xea, 0xe1, 0xbc, 0xab, 0xf8, 0xae, 0x62, 0x4d, 0x19, 0xd0, 0x30, 0x08, 0x4d, 0x13, 0xc3, + 0x94, 0x4b, 0xf2, 0x12, 0xca, 0xcc, 0x57, 0x40, 0xfa, 0x10, 0xdd, 0x3c, 0x9a, 0x77, 0x83, 0xc4, + 0x41, 0xe0, 0x32, 0x87, 0xc6, 0xca, 0x38, 0x61, 0x05, 0xe3, 0x80, 0x1f, 0xb2, 0x50, 0x8c, 0xcc, + 0x26, 0x3a, 0x4c, 0x71, 0xc8, 0x11, 0xd4, 0x9d, 0x11, 0x0f, 0x26, 0xb6, 0x3a, 0x8e, 0xf9, 0x09, + 0x3a, 0xff, 0x6c, 0xde, 0xf9, 0x01, 0x6a, 0xf5, 0xa6, 0x17, 0x91, 0x3d, 0x09, 0xc7, 0x9e, 0x3f, + 0xa4, 0x19, 0x43, 0x99, 0xdd, 0xab, 0xa9, 0x2d, 0x5b, 0x84, 0xf9, 0x08, 0x13, 0x10, 0x93, 0xd6, + 0x63, 0x28, 0x69, 0x1d, 0x80, 0xd2, 0x49, 0xb7, 0x7d, 0x74, 0xde, 0x33, 0x96, 0x48, 0x19, 0x0a, + 0x27, 0xdd, 0x5d, 0x23, 0x67, 0xfd, 0x1e, 0xca, 0xf1, 0x1d, 0xdf, 0x83, 0xd5, 0xf6, 0xe9, 0xc1, + 0xd9, 0x61, 0x9b, 0xf6, 0x0f, 0xdb, 0xaf, 0xf7, 0xde, 0xbe, 0x91, 0x03, 0xea, 0x1a, 0x34, 0x8e, + 0x5b, 0x2f, 0x77, 0xfb, 0xfb, 0x7b, 0xbd, 0xf6, 0x9b, 0xce, 0x69, 0xdb, 0xc8, 0x91, 0x06, 0x54, + 0x91, 0x75, 0xb2, 0xd7, 0x39, 0x35, 0xf2, 0x09, 0x79, 0xdc, 0x39, 0x3a, 0x36, 0x0a, 0xe4, 0x21, + 0xac, 0x23, 0x79, 0x70, 0x76, 0xda, 0x3b, 0xa7, 0x7b, 0x9d, 0xd3, 0xf6, 0xa1, 0x12, 0x15, 0xad, + 0x16, 0xc0, 0x2c, 0x49, 0xa4, 0x02, 0x45, 0xa9, 0x68, 0x2c, 0xe9, 0xd5, 0x0b, 0x23, 0x27, 0xc3, + 0x7a, 0xd7, 0xfd, 0xc6, 0xc8, 0xab, 0xc5, 0x2b, 0xa3, 0x60, 0x1d, 0xc0, 0xda, 0xdc, 0xd9, 0xc9, + 0x0a, 0xc0, 0xc1, 0x31, 0x3d, 0x3b, 0xd9, 0xeb, 0xef, 0xb6, 0x9e, 0x1b, 0x4b, 0x19, 0xba, 0x65, + 0xe4, 0xd2, 0xf4, 0xee, 0xae, 0x91, 0xb7, 0xae, 0x60, 0x3d, 0xfe, 0x0a, 0x61, 0x6e, 0x4f, 0x3d, + 0x29, 0xc4, 0x61, 0x03, 0x0a, 0x53, 0x3e, 0x8e, 0x07, 0x82, 0x29, 0x1f, 0xe3, 0x24, 0x8d, 0x13, + 0xa9, 0x06, 0x5f, 0x4d, 0x91, 0x6d, 0xb8, 0x77, 0x0b, 0xb6, 0xfa, 0xd2, 0x52, 0x8d, 0xdb, 0x6b, + 0x61, 0x06, 0xb6, 0xde, 0xf2, 0xb1, 0xf5, 0x6b, 0x68, 0x24, 0x5b, 0xe2, 0x56, 0x2f, 0xa1, 0xa2, + 0x1f, 0x73, 0x3c, 0x00, 0x35, 0x55, 0xa7, 0x5d, 0x14, 0x18, 0x4d, 0x74, 0xe7, 0x3f, 0x79, 0xac, + 0x3f, 0xe7, 0x60, 0x35, 0xb1, 0xa2, 0x2c, 0x9a, 0x8e, 0x45, 0xdc, 0x30, 0x72, 0xb3, 0x86, 0xb1, + 0x01, 0xcb, 0x8c, 0xf3, 0x80, 0xab, 0x46, 0x75, 0xbc, 0x44, 0x15, 0x49, 0x9e, 0x42, 0xd1, 0xb5, + 0x85, 0xad, 0x1b, 0x37, 0xc9, 0xc6, 0x20, 0xf7, 0x3e, 0x5e, 0xa2, 0xa8, 0x41, 0xbe, 0x84, 0x62, + 0xea, 0xdb, 0x66, 0x5d, 0x21, 0xef, 0xad, 0x29, 0x83, 0xa2, 0xca, 0x7e, 0x05, 0x4a, 0x1c, 0x03, + 0xb1, 0xfe, 0x00, 0xab, 0x94, 0x0d, 0xbd, 0x48, 0xb0, 0xe4, 0x73, 0x6e, 0x03, 0x4a, 0x11, 0x73, + 0x38, 0x8b, 0x3f, 0x62, 0x34, 0x25, 0x1b, 0x92, 0x9e, 0xb2, 0x6f, 0x74, 0xb2, 0x13, 0xfa, 0x63, + 0x3f, 0xeb, 0xfe, 0x98, 0x83, 0xc6, 0x69, 0x20, 0xbc, 0xc1, 0x8d, 0x4e, 0xe6, 0x82, 0x1b, 0xfe, + 0x02, 0xca, 0x91, 0x6a, 0xc3, 0xda, 0x6b, 0x3d, 0x06, 0x5e, 0xcc, 0x7c, 0x2c, 0x94, 0x61, 0x0b, + 0x3b, 0xba, 0xec, 0xb8, 0x98, 0x80, 0x02, 0xd5, 0x54, 0xa6, 0xeb, 0xae, 0x65, 0xbb, 0xee, 0x77, + 0xc5, 0x4a, 0xde, 0x28, 0x7c, 0x57, 0xac, 0x3c, 0x31, 0x2c, 0xeb, 0x2f, 0x79, 0xa8, 0xa7, 0xc7, + 0x28, 0xf9, 0x29, 0xc3, 0x99, 0xe3, 0x85, 0x1e, 0xf3, 0x85, 0xee, 0xf9, 0x33, 0x86, 0x9c, 0x2e, + 0x06, 0xb6, 0xc3, 0xfa, 0xb3, 0x59, 0xb0, 0x4e, 0xab, 0x92, 0xf3, 0x4e, 0x32, 0xc8, 0x43, 0xa8, + 0xbc, 0xf7, 0xfc, 0x7e, 0xc8, 0x83, 0x0b, 0x3d, 0x03, 0x94, 0xdf, 0x7b, 0x7e, 0x97, 0x07, 0x17, + 0xb2, 0x34, 0x13, 0x37, 0x7d, 0x6e, 0xfb, 0xae, 0xea, 0xaa, 0x6a, 0x22, 0x58, 0x4b, 0x44, 0xd4, + 0xf6, 0x5d, 0x6c, 0xaa, 0x04, 0x8a, 0x11, 0x63, 0xae, 0x9e, 0x0d, 0x70, 0x4d, 0xbe, 0x04, 0x63, + 0x36, 0xaa, 0xf4, 0x2f, 0xc6, 0x81, 0x73, 0x89, 0x43, 0x42, 0x9d, 0xae, 0xce, 0xf8, 0xfb, 0x92, + 0x4d, 0x8e, 0x61, 0x2d, 0xa5, 0xaa, 0x67, 0x47, 0x35, 0x30, 0x7c, 0x92, 0x9a, 0x1d, 0xdb, 0x89, + 0x8e, 0x9e, 0x22, 0x53, 0x1b, 0x28, 0x8e, 0xd5, 0x01, 0xa2, 0x74, 0x7b, 0xcc, 0x77, 0x19, 0xd7, + 0x69, 0x7a, 0x02, 0xf5, 0x08, 0xe9, 0xbe, 0x1f, 0xf8, 0x0e, 0xd3, 0xa3, 0x72, 0x4d, 0xf1, 0x4e, + 0x25, 0x6b, 0xc1, 0x9b, 0xf8, 0x01, 0x36, 0x16, 0x6f, 0x4b, 0xb6, 0x60, 0xc5, 0xe1, 0x4c, 0x05, + 0xcb, 0x83, 0xa9, 0xef, 0xea, 0x47, 0xd2, 0x88, 0xb9, 0x54, 0x32, 0xc9, 0x2b, 0x78, 0x98, 0x55, + 0x53, 0x49, 0x50, 0xa9, 0x54, 0x1b, 0x6d, 0x64, 0x2c, 0x30, 0x19, 0x32, 0x9f, 0xd6, 0xdf, 0xf2, + 0x50, 0xee, 0xda, 0x37, 0x58, 0x6e, 0x73, 0x43, 0x75, 0xee, 0x6e, 0x43, 0x35, 0xbe, 0x11, 0x79, + 0x40, 0xbd, 0x97, 0xa6, 0x16, 0x27, 0xbb, 0xf0, 0x11, 0xc9, 0x26, 0x1d, 0xb8, 0xaf, 0x23, 0xd3, + 0xd9, 0xd5, 0xce, 0x8a, 0x88, 0x45, 0x0f, 0x52, 0xce, 0xd2, 0xb7, 0x41, 0x89, 0x98, 0xbf, 0xa1, + 0x17, 0xb0, 0xc2, 0xae, 0x43, 0xe6, 0x08, 0xe6, 0xf6, 0x71, 0xd0, 0xd7, 0xa3, 0xfb, 0xed, 0xaf, + 0x80, 0x46, 0xac, 0x85, 0xac, 0xd6, 0xbf, 0x72, 0x50, 0x4f, 0xe3, 0x07, 0xd9, 0x87, 0xd5, 0x23, + 0x26, 0x32, 0x2c, 0x73, 0x0e, 0x65, 0x34, 0x8a, 0x34, 0x17, 0xe3, 0x0f, 0xf9, 0x2d, 0xac, 0x2f, + 0xfc, 0xc7, 0x44, 0xd4, 0x47, 0xfe, 0x8f, 0xfd, 0xce, 0x6a, 0x5a, 0x3f, 0xa6, 0xa2, 0x7e, 0x51, + 0x91, 0xcf, 0xa1, 0xd8, 0x95, 0x2d, 0x47, 0xfd, 0xda, 0x89, 0xff, 0x9f, 0x35, 0xb3, 0x64, 0xeb, + 0x14, 0xe0, 0x7c, 0xf6, 0x65, 0xf5, 0x4b, 0x20, 0x31, 0x06, 0xa6, 0xb8, 0xf7, 0xd1, 0xe4, 0x16, + 0x38, 0x36, 0x15, 0x00, 0x67, 0x30, 0xeb, 0x79, 0x6e, 0xbf, 0xfc, 0x9b, 0xe5, 0xed, 0xaf, 0x7c, + 0x26, 0x2e, 0x4a, 0xf8, 0xff, 0x6e, 0xe7, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xa2, 0xc3, 0xc1, + 0x2e, 0xd3, 0x13, 0x00, 0x00, } diff --git a/net/lp_rpc.proto b/net/lp_rpc.proto index d628df6a2..bd3e6f1ef 100644 --- a/net/lp_rpc.proto +++ b/net/lp_rpc.proto @@ -115,7 +115,6 @@ message Capabilities { // Non-binary general constraints. message Constraints { string minVersion = 1; - bool ignorePreReleaseVersions = 2; } // Non-binary capability constraints, such as supported ranges. diff --git a/server/ai_session.go b/server/ai_session.go index ad1227808..3a6c28207 100644 --- a/server/ai_session.go +++ b/server/ai_session.go @@ -299,7 +299,6 @@ func (sel *AISessionSelector) getSessions(ctx context.Context) ([]*BroadcastSess } caps := core.NewCapabilitiesWithConstraints(append(core.DefaultCapabilities(), sel.cap), nil, core.Constraints{}, capabilityConstraints) caps.SetMinVersionConstraint(sel.node.Capabilities.MinVersionConstraint()) - caps.SetIgnorePreReleaseVersions(sel.node.Capabilities.IgnorePreReleaseVersions()) // Set numOrchs to the pool size so that discovery tries to find maximum # of compatible orchs within a timeout numOrchs := sel.node.OrchestratorPool.Size() diff --git a/server/broadcast.go b/server/broadcast.go index 77fb4b2dc..df854cc48 100755 --- a/server/broadcast.go +++ b/server/broadcast.go @@ -452,7 +452,6 @@ func (bsm *BroadcastSessionsManager) shouldSkipVerification(sessions []*Broadcas func NewSessionManager(ctx context.Context, node *core.LivepeerNode, params *core.StreamParameters, sel BroadcastSessionsSelectorFactory) *BroadcastSessionsManager { if node.Capabilities != nil { params.Capabilities.SetMinVersionConstraint(node.Capabilities.MinVersionConstraint()) - params.Capabilities.SetIgnorePreReleaseVersions(node.Capabilities.IgnorePreReleaseVersions()) } var trustedPoolSize, untrustedPoolSize float64 if node.OrchestratorPool != nil { diff --git a/server/mediaserver.go b/server/mediaserver.go index 797f7dced..1e1b1be15 100644 --- a/server/mediaserver.go +++ b/server/mediaserver.go @@ -452,7 +452,6 @@ func endRTMPStreamHandler(s *LivepeerServer) func(url *url.URL, rtmpStrm stream. return func(url *url.URL, rtmpStrm stream.RTMPVideoStream) error { params := streamParams(rtmpStrm.AppData()) params.Capabilities.SetMinVersionConstraint(s.LivepeerNode.Capabilities.MinVersionConstraint()) - params.Capabilities.SetIgnorePreReleaseVersions(s.LivepeerNode.Capabilities.IgnorePreReleaseVersions()) if params == nil { return errMismatchedParams } From 5c98592553c4a8ece70d8e0185ef599018941a2b Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Wed, 31 Jul 2024 12:27:05 +0200 Subject: [PATCH 149/203] feat: allow Gateways to filter by pre-release This commit gives Gateways the ability to filter by pre-release suffix. When a pre-release suffix is specified in the `OrchMinLivepeerVersion` command line argument the software now also checks the pre-release version suffix on the orchestrator. --- core/capabilities.go | 12 +++++++++--- core/capabilities_test.go | 32 +++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/core/capabilities.go b/core/capabilities.go index 95e8fdb80..74c4f2905 100644 --- a/core/capabilities.go +++ b/core/capabilities.go @@ -405,14 +405,20 @@ func (bcast *Capabilities) LivepeerVersionCompatibleWith(orch *net.Capabilities) return false } - // If minVersion has no pre-release component, ignore pre-release versions by - // default as in go-livepeer, we define post-release suffixes. - if minVer.Prerelease() == "" { + // If minVersion has no pre-release component, ignore pre-release versions. + // If versions match without pre-release, but only minVersion has a suffix, return false. + // Needed because we use post-release suffixes in go-livepeer. + minVerHasSuffix := minVer.Prerelease() != "" + verHasSuffix := ver.Prerelease() != "" + if minVer.Prerelease() == "" || ver.Prerelease() == "" { minVerNoSuffix, _ := minVer.SetPrerelease("") verNoSuffix, _ := ver.SetPrerelease("") minVer = &minVerNoSuffix ver = &verNoSuffix } + if minVer.Equal(ver) && minVerHasSuffix && !verHasSuffix { + return false + } return !ver.LessThan(minVer) } diff --git a/core/capabilities_test.go b/core/capabilities_test.go index 64bdee1e0..c01b5d921 100644 --- a/core/capabilities_test.go +++ b/core/capabilities_test.go @@ -353,6 +353,13 @@ func TestCapability_CompatibleWithNetCap(t *testing.T) { orch.version = "0.4.1-0.21000000000000-06f1f383fb18" assert.True(bcast.CompatibleWith(orch.ToNetCapabilities())) + // broadcaster is not compatible with orchestrator - no pre-release O's version + orch = NewCapabilities(nil, nil) + bcast = NewCapabilities(nil, nil) + bcast.constraints.minVersion = "0.4.1-0.20000000000000-06f1f383fb18" + orch.version = "0.4.1" + assert.False(bcast.CompatibleWith(orch.ToNetCapabilities())) + // broadcaster is not compatible with orchestrator pre-release - old O's version orch = NewCapabilities(nil, nil) bcast = NewCapabilities(nil, nil) @@ -366,7 +373,6 @@ func TestCapability_CompatibleWithNetCap(t *testing.T) { bcast.constraints.minVersion = "0.4.1-0.20000000000000-06f1f383fb18" orch.version = "0.4.1-0.21000000000000-06f1f383fb18" assert.True(bcast.CompatibleWith(orch.ToNetCapabilities())) - } func TestCapability_RoundTrip_Net(t *testing.T) { @@ -599,6 +605,30 @@ func TestLiveeerVersionCompatibleWith(t *testing.T) { transcoderVersion: "nonparsablesemversion", expected: false, }, + { + name: "broadcaster required version has no pre-release", + broadcasterMinVersion: "0.4.1", + transcoderVersion: "0.4.1-0.21000000000000-06f1f383fb18", + expected: true, + }, + { + name: "transcoder version has no pre-release", + broadcasterMinVersion: "0.4.1-0.21000000000000-06f1f383fb18", + transcoderVersion: "0.4.1", + expected: false, + }, + { + name: "broadcaster required pre-release version is higher than transcoder pre-release version", + broadcasterMinVersion: "0.4.1-0.21000000000000-06f1f383fb18", + transcoderVersion: "0.4.1-0.20000000000000-06f1f383fb18", + expected: false, + }, + { + name: "broadcaster required pre-release version is lower than transcoder pre-release version", + broadcasterMinVersion: "0.4.1-0.20000000000000-06f1f383fb18", + transcoderVersion: "0.4.1-0.21000000000000-06f1f383fb18", + expected: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From d64f378435f0b1048cf2246cdb065ef1ffeb3089 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Wed, 31 Jul 2024 14:14:32 +0200 Subject: [PATCH 150/203] refactor(ai): rename capability constraint variable This commit renames the orchConstraints variable in the capabilities.go file to better reflect that it are constraints per capabilities. --- core/capabilities.go | 4 ++-- server/rpc.go | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/core/capabilities.go b/core/capabilities.go index 74c4f2905..57ec639b2 100644 --- a/core/capabilities.go +++ b/core/capabilities.go @@ -447,8 +447,8 @@ func (bcast *Capabilities) CompatibleWith(orch *net.Capabilities) bool { return false } - orchConstraints := CapabilitiesFromNetCapabilities(orch).capabilityConstraints - if !bcast.capabilityConstraints.CompatibleWith(orchConstraints) { + orchCapabilityConstraints := CapabilitiesFromNetCapabilities(orch).capabilityConstraints + if !bcast.capabilityConstraints.CompatibleWith(orchCapabilityConstraints) { return false } diff --git a/server/rpc.go b/server/rpc.go index 0ab18f4a1..0c8b9f806 100644 --- a/server/rpc.go +++ b/server/rpc.go @@ -338,7 +338,6 @@ func getOrchestrator(orch Orchestrator, req *net.OrchestratorRequest) (*net.Orch } // currently, orchestrator == transcoder - if req.Capabilities == nil { return orchestratorInfo(orch, addr, orch.ServiceURI().String(), "") } From bac48c5e321c01fd936300d53ed9d9d5642869e3 Mon Sep 17 00:00:00 2001 From: Josh Allmann Date: Thu, 25 Jul 2024 21:36:12 +0000 Subject: [PATCH 151/203] cmd: Use `-gateway` consistently Avoid references to deprecated CLI flags. --- cmd/devtool/devtool.go | 2 +- cmd/livepeer/starter/starter.go | 2 +- doc/verification.md | 2 +- test_args.sh | 82 ++++++++++++++++----------------- 4 files changed, 44 insertions(+), 44 deletions(-) diff --git a/cmd/devtool/devtool.go b/cmd/devtool/devtool.go index 038313350..7052ed032 100644 --- a/cmd/devtool/devtool.go +++ b/cmd/devtool/devtool.go @@ -229,7 +229,7 @@ func createRunScript(ethController string, dataDir, serviceHost string, cfg devt } else { args = append( args, - "-broadcaster=true", + "-gateway=true", fmt.Sprintf("-rtmpAddr %s:%d", serviceHost, rtmpPort), ) diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index 1495101ce..7568f1f13 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -666,7 +666,7 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { } else if *cfg.Gateway { n.NodeType = core.BroadcasterNode } else if (cfg.Reward == nil || !*cfg.Reward) && !*cfg.InitializeRound { - exit("No services enabled; must be at least one of -broadcaster, -transcoder, -orchestrator, -redeemer, -reward or -initializeRound") + exit("No services enabled; must be at least one of -gateway, -transcoder, -orchestrator, -redeemer, -reward or -initializeRound") } lpmon.NodeID = *cfg.EthAcctAddr diff --git a/doc/verification.md b/doc/verification.md index b1085bebc..359d5968c 100644 --- a/doc/verification.md +++ b/doc/verification.md @@ -1,6 +1,6 @@ # Verification -The Livepeer node supports two types of verification when running with the `-broadcaster` flag: +The Livepeer node supports two types of verification when running with the `-gateway` flag: - Local verification - This currently involves pixel count and signature verification. diff --git a/test_args.sh b/test_args.sh index 6e1da4154..ca86d6304 100755 --- a/test_args.sh +++ b/test_args.sh @@ -37,7 +37,7 @@ res=0 $TMPDIR/livepeer || res=$? [ $res -ne 0 ] -run_lp -broadcaster +run_lp -gateway [ -d "$DEFAULT_DATADIR"/offchain ] kill $pid @@ -45,7 +45,7 @@ kill $pid [ ! -d "$CUSTOM_DATADIR" ] # check custom datadir without a network (offchain) -run_lp -broadcaster -dataDir "$CUSTOM_DATADIR" +run_lp -gateway -dataDir "$CUSTOM_DATADIR" [ -d "$CUSTOM_DATADIR" ] [ ! -d "$CUSTOM_DATADIR"/offchain ] # sanity check that network isn't included kill $pid @@ -83,13 +83,13 @@ if [ -z ${MAINNET_ETH_URL+x} ]; then else # Exit early if -ethUrl is missing res=0 - $TMPDIR/livepeer -broadcaster -network mainnet $ETH_ARGS || res=$? + $TMPDIR/livepeer -gateway -network mainnet $ETH_ARGS || res=$? [ $res -ne 0 ] OLD_ETH_ARGS=$ETH_ARGS ETH_ARGS="${ETH_ARGS} -ethUrl ${MAINNET_ETH_URL}" - run_lp -broadcaster -network mainnet $ETH_ARGS + run_lp -gateway -network mainnet $ETH_ARGS [ -d "$DEFAULT_DATADIR"/mainnet ] kill $pid @@ -102,27 +102,27 @@ if [ -z ${RINKEBY_ETH_URL+x} ]; then else # Exit early if -ethUrl is missing res=0 - $TMPDIR/livepeer -broadcaster -network rinkeby $ETH_ARGS || res=$? + $TMPDIR/livepeer -gateway -network rinkeby $ETH_ARGS || res=$? [ $res -ne 0 ] OLD_ETH_ARGS=$ETH_ARGS ETH_ARGS="${ETH_ARGS} -ethUrl ${RINKEBY_ETH_URL}" - run_lp -broadcaster -network rinkeby $ETH_ARGS + run_lp -gateway -network rinkeby $ETH_ARGS [ -d "$DEFAULT_DATADIR"/rinkeby ] kill $pid # Error if flags to set MaxBroadcastPrice aren't provided correctly res=0 - $TMPDIR/livepeer -broadcaster -network rinkeby $ETH_ARGS -maxPricePerUnit 0 -pixelsPerUnit -5 || res=$? + $TMPDIR/livepeer -gateway -network rinkeby $ETH_ARGS -maxPricePerUnit 0 -pixelsPerUnit -5 || res=$? [ $res -ne 0 ] - run_lp -broadcaster -network anyNetwork $ETH_ARGS -v 99 + run_lp -gateway -network anyNetwork $ETH_ARGS -v 99 [ -d "$DEFAULT_DATADIR"/anyNetwork ] kill $pid # check custom datadir with a network - run_lp -broadcaster -dataDir "$CUSTOM_DATADIR" -network rinkeby $ETH_ARGS + run_lp -gateway -dataDir "$CUSTOM_DATADIR" -network rinkeby $ETH_ARGS [ ! -d "$CUSTOM_DATADIR"/rinkeby ] # sanity check that network isn't included kill $pid @@ -141,22 +141,22 @@ else # Broadcaster needs a valid rational number for -maxTicketEV res=0 - $TMPDIR/livepeer -broadcaster -maxTicketEV abcd -network rinkeby $ETH_ARGS || res=$? + $TMPDIR/livepeer -gateway -maxTicketEV abcd -network rinkeby $ETH_ARGS || res=$? [ $res -ne 0 ] # Broadcaster needs a non-negative number for -maxTicketEV res=0 - $TMPDIR/livepeer -broadcaster -maxTicketEV -1 -network rinkeby $ETH_ARGS || res=$? + $TMPDIR/livepeer -gateway -maxTicketEV -1 -network rinkeby $ETH_ARGS || res=$? [ $res -ne 0 ] # Broadcaster needs a positive number for -depositMultiplier res=0 - $TMPDIR/livepeer -broadcaster -depositMultiplier 0 -network rinkeby $ETH_ARGS || res=$? + $TMPDIR/livepeer -gateway -depositMultiplier 0 -network rinkeby $ETH_ARGS || res=$? [ $res -ne 0 ] # Check that local verification is enabled by default in on-chain mode - $TMPDIR/livepeer -broadcaster -transcodingOptions invalid -network rinkeby $ETH_ARGS 2>&1 | grep "Local verification enabled" + $TMPDIR/livepeer -gateway -transcodingOptions invalid -network rinkeby $ETH_ARGS 2>&1 | grep "Local verification enabled" # Check that local verification is disabled via -localVerify in on-chain mode - $TMPDIR/livepeer -broadcaster -transcodingOptions invalid -localVerify=false -network rinkeby $ETH_ARGS 2>&1 | grep -v "Local verification enabled" + $TMPDIR/livepeer -gateway -transcodingOptions invalid -localVerify=false -network rinkeby $ETH_ARGS 2>&1 | grep -v "Local verification enabled" ETH_ARGS=$OLD_ETH_ARGS fi @@ -168,101 +168,101 @@ $TMPDIR/livepeer -transcoder || res=$? # exit early if webhook url is not http res=0 -$TMPDIR/livepeer -broadcaster -authWebhookUrl tcp://host/ || res=$? +$TMPDIR/livepeer -gateway -authWebhookUrl tcp://host/ || res=$? [ $res -ne 0 ] # exit early if webhook url is not properly formatted res=0 -$TMPDIR/livepeer -broadcaster -authWebhookUrl http\\://host/ || res=$? +$TMPDIR/livepeer -gateway -authWebhookUrl http\\://host/ || res=$? [ $res -ne 0 ] # exit early if orchestrator webhook URL is not http res=0 -$TMPDIR/livepeer -broadcaster -orchWebhookUrl tcp://host/ || res=$? +$TMPDIR/livepeer -gateway -orchWebhookUrl tcp://host/ || res=$? [ $res -ne 0 ] # exit early if orchestrator webhook URL is not properly formatted res=0 -$TMPDIR/livepeer -broadcaster -orchWebhookUrl http\\://host/ || res=$? +$TMPDIR/livepeer -gateway -orchWebhookUrl http\\://host/ || res=$? [ $res -ne 0 ] # exit early if maxSessions less or equal to zero res=0 -$TMPDIR/livepeer -broadcaster -maxSessions -1 || res=$? +$TMPDIR/livepeer -gateway -maxSessions -1 || res=$? [ $res -ne 0 ] res=0 -$TMPDIR/livepeer -broadcaster -maxSessions 0 || res=$? +$TMPDIR/livepeer -gateway -maxSessions 0 || res=$? [ $res -ne 0 ] # Check that pprof is running on CLI port -run_lp -broadcaster +run_lp -gateway curl -sI http://127.0.0.1:5935/debug/pprof/allocs | grep "200 OK" kill $pid # exit early if verifier URL is not http res=0 -$TMPDIR/livepeer -broadcaster -verifierUrl tcp://host/ || res=$? +$TMPDIR/livepeer -gateway -verifierUrl tcp://host/ || res=$? [ $res -ne 0 ] # exit early if verifier URL is not properly formatted res=0 -$TMPDIR/livepeer -broadcaster -verifierUrl http\\://host/ || res=$? +$TMPDIR/livepeer -gateway -verifierUrl http\\://host/ || res=$? [ $res -ne 0 ] # Check that verifier shared path is required -$TMPDIR/livepeer -broadcaster -verifierUrl http://host 2>&1 | grep "Requires a path to the" +$TMPDIR/livepeer -gateway -verifierUrl http://host 2>&1 | grep "Requires a path to the" # Check OK with verifier shared path -run_lp -broadcaster -verifierUrl http://host -verifierPath path +run_lp -gateway -verifierUrl http://host -verifierPath path kill $pid # Check OK with verifier + external storage -run_lp -broadcaster -verifierUrl http://host -objectStore s3+https://ACCESS_KEY_ID:ACCESS_KEY_PASSWORD@s3api.example.com/bucket-name +run_lp -gateway -verifierUrl http://host -objectStore s3+https://ACCESS_KEY_ID:ACCESS_KEY_PASSWORD@s3api.example.com/bucket-name kill $pid # Check that HTTP ingest is disabled when -httpAddr is publicly accessible and there is no auth webhook URL and -httpIngest defaults to false -run_lp -broadcaster -httpAddr 0.0.0.0 +run_lp -gateway -httpAddr 0.0.0.0 curl -X PUT http://localhost:9935/live/movie/0.ts | grep "404 page not found" kill $pid # Check that HTTP ingest is disabled when -httpAddr is not publicly accessible and -httpIngest is set to false -run_lp -broadcaster -httpIngest=false +run_lp -gateway -httpIngest=false curl -X PUT http://localhost:9935/live/movie/0.ts | grep "404 page not found" kill $pid # Check that HTTP ingest is disabled when -httpAddr is publicly accessible and there is a auth webhook URL and -httpIngest is set to false -run_lp -broadcaster -httpAddr 0.0.0.0 -authWebhookUrl http://foo.com -httpIngest=false +run_lp -gateway -httpAddr 0.0.0.0 -authWebhookUrl http://foo.com -httpIngest=false curl -X PUT http://localhost:9935/live/movie/0.ts | grep "404 page not found" kill $pid # Check that HTTP ingest is enabled when -httpIngest is true -run_lp -broadcaster -httpAddr 0.0.0.0 -httpIngest +run_lp -gateway -httpAddr 0.0.0.0 -httpIngest curl -X PUT http://localhost:9935/live/movie/0.ts | grep -v "404 page not found" kill $pid # Check that HTTP ingest is enabled when -httpAddr sets the hostname to 127.0.0.1 -run_lp -broadcaster -httpAddr 127.0.0.1 +run_lp -gateway -httpAddr 127.0.0.1 curl -X PUT http://localhost:9935/live/movie/0.ts | grep -v "404 page not found" kill $pid # Check that HTTP ingest is enabled when -httpAddr sets the hostname to localhost -run_lp -broadcaster -httpAddr localhost +run_lp -gateway -httpAddr localhost curl -X PUT http://localhost:9935/live/movie/0.ts | grep -v "404 page not found" kill $pid # Check that HTTP ingest is enabled when there is an auth webhook URL -run_lp -broadcaster -httpAddr 0.0.0.0 -authWebhookUrl http://foo.com +run_lp -gateway -httpAddr 0.0.0.0 -authWebhookUrl http://foo.com curl -X PUT http://localhost:9935/live/movie/0.ts | grep -v "404 page not found" kill $pid # Check that the default presets are used -run_lp -broadcaster +run_lp -gateway curl -s --stderr - http://localhost:5935/getBroadcastConfig | grep P240p30fps16x9,P360p30fps16x9 kill $pid # Check that the presets passed in are used -run_lp -broadcaster -transcodingOptions P144p30fps16x9,P720p30fps16x9 +run_lp -gateway -transcodingOptions P144p30fps16x9,P720p30fps16x9 curl -s --stderr - http://localhost:5935/getBroadcastConfig | grep P144p30fps16x9,P720p30fps16x9 kill $pid @@ -270,25 +270,25 @@ kill $pid cat >$TMPDIR/profile.json <&1 | grep "No transcoding profiles found" +$TMPDIR/livepeer -gateway -transcodingOptions notarealfile 2>&1 | grep "No transcoding profiles found" # Check that it fails out on an malformed profiles json echo "not json" >$TMPDIR/invalid.json -$TMPDIR/livepeer -broadcaster -transcodingOptions $TMPDIR/invalid.json 2>&1 | grep "invalid character" +$TMPDIR/livepeer -gateway -transcodingOptions $TMPDIR/invalid.json 2>&1 | grep "invalid character" # Check that it fails out on an invalid schema - width / height as strings echo '[{"width":"1","height":"2"}]' >$TMPDIR/schema.json -$TMPDIR/livepeer -broadcaster -transcodingOptions $TMPDIR/schema.json 2>&1 | grep "cannot unmarshal string into Go struct field JsonProfile.width of type int" +$TMPDIR/livepeer -gateway -transcodingOptions $TMPDIR/schema.json 2>&1 | grep "cannot unmarshal string into Go struct field JsonProfile.width of type int" # Check that local verification is disabled by default in off-chain mode -$TMPDIR/livepeer -broadcaster -transcodingOptions invalid 2>&1 | grep -v "Local verification enabled" +$TMPDIR/livepeer -gateway -transcodingOptions invalid 2>&1 | grep -v "Local verification enabled" # Check that local verification is enabled via -localVerify in off-chain mode -$TMPDIR/livepeer -broadcaster -transcodingOptions invalid -localVerify=true 2>&1 | grep "Local verification enabled" +$TMPDIR/livepeer -gateway -transcodingOptions invalid -localVerify=true 2>&1 | grep "Local verification enabled" rm -rf $TMPDIR From bd55cd2eb38de2bf4b46e0ff456dfb7c05b7bfbe Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Wed, 31 Jul 2024 15:58:59 +0200 Subject: [PATCH 152/203] chore(ai): remove lpms local dependency This commit ensure that the right remote dependency is used for the lpms package. --- go.mod | 4 +--- go.sum | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 52fcd8672..1c7ad3359 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/livepeer/ai-worker v0.1.0 github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 - github.com/livepeer/lpms v0.0.0-20240711175220-227325841434 + github.com/livepeer/lpms v0.0.0-20240726132931-5b7b9f5e831f github.com/livepeer/m3u8 v0.11.1 github.com/mattn/go-sqlite3 v1.14.18 github.com/oapi-codegen/nethttp-middleware v1.0.1 @@ -238,5 +238,3 @@ require ( lukechampine.com/blake3 v1.2.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect ) - -replace github.com/livepeer/lpms => /home/ricks/development/livepeer/ai/lpms diff --git a/go.sum b/go.sum index fee65b0c5..7054636b3 100644 --- a/go.sum +++ b/go.sum @@ -631,6 +631,8 @@ github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cO github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded/go.mod h1:xkDdm+akniYxVT9KW1Y2Y7Hso6aW+rZObz3nrA9yTHw= github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 h1:4oH3NqV0NvcdS44Ld3zK2tO8IUiNozIggm74yobQeZg= github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18/go.mod h1:Jpf4jHK+fbWioBHRDRM1WadNT1qmY27g2YicTdO0Rtc= +github.com/livepeer/lpms v0.0.0-20240726132931-5b7b9f5e831f h1:zAlqPgRkSo6zubbZS2SpOpO4oPGprr3zoabVK6tANMg= +github.com/livepeer/lpms v0.0.0-20240726132931-5b7b9f5e831f/go.mod h1:z5ROP1l5OzAKSoqVRLc34MjUdueil6wHSecQYV7llIw= github.com/livepeer/m3u8 v0.11.1 h1:VkUJzfNTyjy9mqsgp5JPvouwna8wGZMvd/gAfT5FinU= github.com/livepeer/m3u8 v0.11.1/go.mod h1:IUqAtwWPAG2CblfQa4SVzTQoDcEMPyfNOaBSxqHMS04= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= From 8c6bd5ce47b4dee1533d27ff5a4ac42bf3b9623e Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Wed, 31 Jul 2024 16:03:01 +0200 Subject: [PATCH 153/203] chore(ai): update lpms dependency This commit updates the lpms dependency to the lastest version of the `ai-video-rebase-main`. --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1c7ad3359..1a59fc36f 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/livepeer/ai-worker v0.1.0 github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 - github.com/livepeer/lpms v0.0.0-20240726132931-5b7b9f5e831f + github.com/livepeer/lpms v0.0.0-20240731140137-28406cf8bc78 github.com/livepeer/m3u8 v0.11.1 github.com/mattn/go-sqlite3 v1.14.18 github.com/oapi-codegen/nethttp-middleware v1.0.1 diff --git a/go.sum b/go.sum index 7054636b3..2e9eacc71 100644 --- a/go.sum +++ b/go.sum @@ -631,8 +631,8 @@ github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cO github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded/go.mod h1:xkDdm+akniYxVT9KW1Y2Y7Hso6aW+rZObz3nrA9yTHw= github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 h1:4oH3NqV0NvcdS44Ld3zK2tO8IUiNozIggm74yobQeZg= github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18/go.mod h1:Jpf4jHK+fbWioBHRDRM1WadNT1qmY27g2YicTdO0Rtc= -github.com/livepeer/lpms v0.0.0-20240726132931-5b7b9f5e831f h1:zAlqPgRkSo6zubbZS2SpOpO4oPGprr3zoabVK6tANMg= -github.com/livepeer/lpms v0.0.0-20240726132931-5b7b9f5e831f/go.mod h1:z5ROP1l5OzAKSoqVRLc34MjUdueil6wHSecQYV7llIw= +github.com/livepeer/lpms v0.0.0-20240731140137-28406cf8bc78 h1:J026gPdu533qY+KNEgWaSiu3dExW+DB4UyE8+PAEKvg= +github.com/livepeer/lpms v0.0.0-20240731140137-28406cf8bc78/go.mod h1:z5ROP1l5OzAKSoqVRLc34MjUdueil6wHSecQYV7llIw= github.com/livepeer/m3u8 v0.11.1 h1:VkUJzfNTyjy9mqsgp5JPvouwna8wGZMvd/gAfT5FinU= github.com/livepeer/m3u8 v0.11.1/go.mod h1:IUqAtwWPAG2CblfQa4SVzTQoDcEMPyfNOaBSxqHMS04= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= From 95c3b6d142cd05fbe626aa82fbed2f1af11ee1f6 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 1 Aug 2024 22:28:17 +0200 Subject: [PATCH 154/203] feat: remove AI specific minVersion constraint logic This commit removes the ai-specific logic that allows ai suffixes to work during version constraint checking. This was done to add it back in a seperate commit so that we can always revert. --- core/capabilities.go | 19 +++----------- core/capabilities_test.go | 52 --------------------------------------- 2 files changed, 4 insertions(+), 67 deletions(-) diff --git a/core/capabilities.go b/core/capabilities.go index 57ec639b2..a53526408 100644 --- a/core/capabilities.go +++ b/core/capabilities.go @@ -405,22 +405,11 @@ func (bcast *Capabilities) LivepeerVersionCompatibleWith(orch *net.Capabilities) return false } - // If minVersion has no pre-release component, ignore pre-release versions. - // If versions match without pre-release, but only minVersion has a suffix, return false. - // Needed because we use post-release suffixes in go-livepeer. - minVerHasSuffix := minVer.Prerelease() != "" - verHasSuffix := ver.Prerelease() != "" - if minVer.Prerelease() == "" || ver.Prerelease() == "" { - minVerNoSuffix, _ := minVer.SetPrerelease("") - verNoSuffix, _ := ver.SetPrerelease("") - minVer = &minVerNoSuffix - ver = &verNoSuffix - } - if minVer.Equal(ver) && minVerHasSuffix && !verHasSuffix { - return false - } + // Ignore prerelease versions as in go-livepeer we actually define post-release suffixes + minVerNoSuffix, _ := minVer.SetPrerelease("") + verNoSuffix, _ := ver.SetPrerelease("") - return !ver.LessThan(minVer) + return !verNoSuffix.LessThan(&minVerNoSuffix) } func (bcast *Capabilities) CompatibleWith(orch *net.Capabilities) bool { diff --git a/core/capabilities_test.go b/core/capabilities_test.go index c01b5d921..377b074a5 100644 --- a/core/capabilities_test.go +++ b/core/capabilities_test.go @@ -345,34 +345,6 @@ func TestCapability_CompatibleWithNetCap(t *testing.T) { bcast.constraints.minVersion = "0.4.1" orch.version = "0.4.1" assert.True(bcast.CompatibleWith(orch.ToNetCapabilities())) - - // broadcaster is compatible with orchestrator - no pre-release min version suffix - orch = NewCapabilities(nil, nil) - bcast = NewCapabilities(nil, nil) - bcast.constraints.minVersion = "0.4.1" - orch.version = "0.4.1-0.21000000000000-06f1f383fb18" - assert.True(bcast.CompatibleWith(orch.ToNetCapabilities())) - - // broadcaster is not compatible with orchestrator - no pre-release O's version - orch = NewCapabilities(nil, nil) - bcast = NewCapabilities(nil, nil) - bcast.constraints.minVersion = "0.4.1-0.20000000000000-06f1f383fb18" - orch.version = "0.4.1" - assert.False(bcast.CompatibleWith(orch.ToNetCapabilities())) - - // broadcaster is not compatible with orchestrator pre-release - old O's version - orch = NewCapabilities(nil, nil) - bcast = NewCapabilities(nil, nil) - bcast.constraints.minVersion = "0.4.1-0.21000000000000-06f1f383fb18" - orch.version = "0.4.1-0.20000000000000-06f1f383fb18" - assert.False(bcast.CompatibleWith(orch.ToNetCapabilities())) - - // broadcaster is compatible with orchestrator pre-release - higher O's version - orch = NewCapabilities(nil, nil) - bcast = NewCapabilities(nil, nil) - bcast.constraints.minVersion = "0.4.1-0.20000000000000-06f1f383fb18" - orch.version = "0.4.1-0.21000000000000-06f1f383fb18" - assert.True(bcast.CompatibleWith(orch.ToNetCapabilities())) } func TestCapability_RoundTrip_Net(t *testing.T) { @@ -605,30 +577,6 @@ func TestLiveeerVersionCompatibleWith(t *testing.T) { transcoderVersion: "nonparsablesemversion", expected: false, }, - { - name: "broadcaster required version has no pre-release", - broadcasterMinVersion: "0.4.1", - transcoderVersion: "0.4.1-0.21000000000000-06f1f383fb18", - expected: true, - }, - { - name: "transcoder version has no pre-release", - broadcasterMinVersion: "0.4.1-0.21000000000000-06f1f383fb18", - transcoderVersion: "0.4.1", - expected: false, - }, - { - name: "broadcaster required pre-release version is higher than transcoder pre-release version", - broadcasterMinVersion: "0.4.1-0.21000000000000-06f1f383fb18", - transcoderVersion: "0.4.1-0.20000000000000-06f1f383fb18", - expected: false, - }, - { - name: "broadcaster required pre-release version is lower than transcoder pre-release version", - broadcasterMinVersion: "0.4.1-0.20000000000000-06f1f383fb18", - transcoderVersion: "0.4.1-0.21000000000000-06f1f383fb18", - expected: true, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From 4c4b1b6aa58f03155d1211d7cc44ba4d2af327fb Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 1 Aug 2024 22:36:20 +0200 Subject: [PATCH 155/203] feat(ai): Add AI suffix handling for min version logic This commit introduces logic to handle version constraints with AI-specific suffixes (e.g., v0.7.6-ai.1). This ensures compatibility during version constraint filtering. Note that this logic should be removed when merging into the master branch. --- core/capabilities.go | 25 ++++++++++-- core/capabilities_test.go | 84 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 4 deletions(-) diff --git a/core/capabilities.go b/core/capabilities.go index a53526408..237790e3c 100644 --- a/core/capabilities.go +++ b/core/capabilities.go @@ -405,11 +405,28 @@ func (bcast *Capabilities) LivepeerVersionCompatibleWith(orch *net.Capabilities) return false } - // Ignore prerelease versions as in go-livepeer we actually define post-release suffixes - minVerNoSuffix, _ := minVer.SetPrerelease("") - verNoSuffix, _ := ver.SetPrerelease("") + // // Ignore prerelease versions as in go-livepeer we actually define post-release suffixes + // minVerNoSuffix, _ := minVer.SetPrerelease("") + // verNoSuffix, _ := ver.SetPrerelease("") + + // return !verNoSuffix.LessThan(&minVerNoSuffix) + + // TODO: Remove AI-specific cases below when merging into master. + // NOTE: This logic was added to allow the version suffix (i.e. v0.7.6-ai.1) to be + // used correctly during the version constraint filtering. + minVerHasSuffix := minVer.Prerelease() != "" + verHasSuffix := ver.Prerelease() != "" + if !minVerHasSuffix || !verHasSuffix { + minVerNoSuffix, _ := minVer.SetPrerelease("") + verNoSuffix, _ := ver.SetPrerelease("") + minVer = &minVerNoSuffix + ver = &verNoSuffix + } + if minVer.Equal(ver) && minVerHasSuffix && !verHasSuffix { + return false + } - return !verNoSuffix.LessThan(&minVerNoSuffix) + return !ver.LessThan(minVer) } func (bcast *Capabilities) CompatibleWith(orch *net.Capabilities) bool { diff --git a/core/capabilities_test.go b/core/capabilities_test.go index 377b074a5..89e3d6210 100644 --- a/core/capabilities_test.go +++ b/core/capabilities_test.go @@ -345,6 +345,51 @@ func TestCapability_CompatibleWithNetCap(t *testing.T) { bcast.constraints.minVersion = "0.4.1" orch.version = "0.4.1" assert.True(bcast.CompatibleWith(orch.ToNetCapabilities())) + + // TODO: Remove AI-specific cases below when merging into master. + // NOTE: Additional logic was added to the `LivepeerVersionCompatibleWith` method in + // capabilities.go to achieve this behavior. + // AI broadcaster is compatible with AI orchestrator - higher ai suffix + orch = NewCapabilities(nil, nil) + bcast = NewCapabilities(nil, nil) + bcast.constraints.minVersion = "0.7.2" + orch.version = "0.7.2-ai.1" + assert.True(bcast.CompatibleWith(orch.ToNetCapabilities())) + + // AI broadcaster is not compatible with AI orchestrator - no ai suffix + orch = NewCapabilities(nil, nil) + bcast = NewCapabilities(nil, nil) + bcast.constraints.minVersion = "0.7.2-ai.1" + orch.version = "0.7.2" + assert.False(bcast.CompatibleWith(orch.ToNetCapabilities())) + + // AI broadcaster is not compatible with AI orchestrator - lower ai suffix + orch = NewCapabilities(nil, nil) + bcast = NewCapabilities(nil, nil) + bcast.constraints.minVersion = "0.7.2-ai.2" + orch.version = "0.7.2-ai.1" + assert.False(bcast.CompatibleWith(orch.ToNetCapabilities())) + + // AI broadcaster is not compatible with AI orchestrator - lower major version + orch = NewCapabilities(nil, nil) + bcast = NewCapabilities(nil, nil) + bcast.constraints.minVersion = "0.7.2-ai.2" + orch.version = "0.7.1-ai.1" + assert.False(bcast.CompatibleWith(orch.ToNetCapabilities())) + + // AI broadcaster is compatible with AI orchestrator - higher ai suffix + orch = NewCapabilities(nil, nil) + bcast = NewCapabilities(nil, nil) + bcast.constraints.minVersion = "0.7.2-ai.1" + orch.version = "0.7.2-ai.2" + assert.True(bcast.CompatibleWith(orch.ToNetCapabilities())) + + // AI broadcaster is compatible with AI orchestrator- higher major version + orch = NewCapabilities(nil, nil) + bcast = NewCapabilities(nil, nil) + bcast.constraints.minVersion = "0.7.2-ai.2" + orch.version = "0.7.3-ai.1" + assert.True(bcast.CompatibleWith(orch.ToNetCapabilities())) } func TestCapability_RoundTrip_Net(t *testing.T) { @@ -577,6 +622,45 @@ func TestLiveeerVersionCompatibleWith(t *testing.T) { transcoderVersion: "nonparsablesemversion", expected: false, }, + // TODO: Remove AI-specific cases below when merging into master. + // NOTE: Additional logic was added to the `LivepeerVersionCompatibleWith` method in + // capabilities.go to achieve this behavior. + { + name: "AI broadcaster required version has no AI suffix", + broadcasterMinVersion: "0.7.2", + transcoderVersion: "0.7.2-ai.1", + expected: true, + }, + { + name: "AI transcoder version has no AI suffix", + broadcasterMinVersion: "0.7.2-ai.1", + transcoderVersion: "0.7.2", + expected: false, + }, + { + name: "AI broadcaster required version AI suffix is higher than AI transcoder AI suffix", + broadcasterMinVersion: "0.7.2-ai.2", + transcoderVersion: "0.7.2-ai.1", + expected: false, + }, + { + name: "AI broadcaster required major version is higher than AI transcoder major version", + broadcasterMinVersion: "0.7.2-ai.2", + transcoderVersion: "0.7.2-ai.1", + expected: false, + }, + { + name: "AI broadcaster required version AI suffix is lower than AI transcoder AI suffix", + broadcasterMinVersion: "0.7.2-ai.1", + transcoderVersion: "0.7.2-ai.2", + expected: true, + }, + { + name: "AI broadcaster required major version is lower than AI transcoder major version", + broadcasterMinVersion: "0.7.2-ai.1", + transcoderVersion: "0.7.3-ai.1", + expected: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From 8036856cbfd6724f79b393118e49fd2451282797 Mon Sep 17 00:00:00 2001 From: Victor Elias Date: Thu, 1 Aug 2024 18:44:15 -0300 Subject: [PATCH 156/203] core: Make the AI config take a big rat --- cmd/livepeer/starter/starter.go | 30 +++++++++----------------- core/ai.go | 37 +++++++++++++-------------------- 2 files changed, 24 insertions(+), 43 deletions(-) diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index 95d00e47e..bd95626b8 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -1704,17 +1704,17 @@ func getGatewayPrices(gatewayPrices string) []GatewayPrice { // {"gateways":[{"ethaddress":"address1","priceperunit":0.5,"currency":"USD","pixelsperunit":1}, {"ethaddress":"address2","priceperunit":0.3,"currency":"USD","pixelsperunit":3}]} var pricesSet struct { Gateways []struct { - EthAddress string `json:"ethaddress"` - PixelsPerUnit json.RawMessage `json:"pixelsperunit"` - PricePerUnit json.RawMessage `json:"priceperunit"` - Currency string `json:"currency"` + EthAddress string `json:"ethaddress"` + PixelsPerUnit core.JSONRat `json:"pixelsperunit"` + PricePerUnit core.JSONRat `json:"priceperunit"` + Currency string `json:"currency"` } `json:"gateways"` // TODO: Keep the old name for backwards compatibility, remove in the future Broadcasters []struct { - EthAddress string `json:"ethaddress"` - PixelsPerUnit json.RawMessage `json:"pixelsperunit"` - PricePerUnit json.RawMessage `json:"priceperunit"` - Currency string `json:"currency"` + EthAddress string `json:"ethaddress"` + PixelsPerUnit core.JSONRat `json:"pixelsperunit"` + PricePerUnit core.JSONRat `json:"priceperunit"` + Currency string `json:"currency"` } `json:"broadcasters"` } pricesFileContent, _ := common.ReadFromFile(gatewayPrices) @@ -1735,21 +1735,11 @@ func getGatewayPrices(gatewayPrices string) []GatewayPrice { prices := make([]GatewayPrice, len(allGateways)) for i, p := range allGateways { - pixelsPerUnit, ok := new(big.Rat).SetString(string(p.PixelsPerUnit)) - if !ok { - glog.Errorf("Pixels per unit could not be parsed for gateway %v. must be a valid number, provided %s", p.EthAddress, p.PixelsPerUnit) - continue - } - pricePerUnit, ok := new(big.Rat).SetString(string(p.PricePerUnit)) - if !ok { - glog.Errorf("Price per unit could not be parsed for gateway %v. must be a valid number, provided %s", p.EthAddress, p.PricePerUnit) - continue - } prices[i] = GatewayPrice{ EthAddress: p.EthAddress, Currency: p.Currency, - PricePerUnit: pricePerUnit, - PixelsPerUnit: pixelsPerUnit, + PricePerUnit: p.PricePerUnit.Rat, + PixelsPerUnit: p.PixelsPerUnit.Rat, } } diff --git a/core/ai.go b/core/ai.go index 93966bd41..5f8a1694a 100644 --- a/core/ai.go +++ b/core/ai.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "math/big" "os" "regexp" "strconv" @@ -24,30 +25,20 @@ type AI interface { HasCapacity(pipeline, modelID string) bool } -// Custom type to handle both string and int but store as string. -type StringInt string +// Custom type to parse a big.Rat from a JSON number. +type JSONRat struct{ *big.Rat } -// UnmarshalJSON method to handle both string and int. -func (s *StringInt) UnmarshalJSON(data []byte) error { - // Try to unmarshal as int. - var intValue int64 - if err := json.Unmarshal(data, &intValue); err == nil { - *s = StringInt(strconv.FormatInt(intValue, 10)) - return nil +func (s *JSONRat) UnmarshalJSON(data []byte) error { + rat, ok := new(big.Rat).SetString(string(data)) + if !ok { + return fmt.Errorf("value is not a number: %s", data) } - - var strValue string - if err := json.Unmarshal(data, &strValue); err == nil { - *s = StringInt(strValue) - return nil - } - - return fmt.Errorf("invalid value for StringInt: %s", data) + *s = JSONRat{rat} + return nil } -// String converts the StringInt type to a string. -func (s StringInt) String() string { - return string(s) +func (s JSONRat) String() string { + return s.FloatString(2) } type AIModelConfig struct { @@ -56,8 +47,8 @@ type AIModelConfig struct { URL string `json:"url,omitempty"` Token string `json:"token,omitempty"` Warm bool `json:"warm,omitempty"` - PricePerUnit StringInt `json:"price_per_unit,omitempty"` - PixelsPerUnit StringInt `json:"pixels_per_unit,omitempty"` + PricePerUnit JSONRat `json:"price_per_unit,omitempty"` + PixelsPerUnit JSONRat `json:"pixels_per_unit,omitempty"` OptimizationFlags worker.OptimizationFlags `json:"optimization_flags,omitempty"` } @@ -66,7 +57,7 @@ func (config *AIModelConfig) UnmarshalJSON(data []byte) error { type AIModelConfigAlias AIModelConfig // Set default values for fields defaultConfig := &AIModelConfigAlias{ - PixelsPerUnit: "1", + PixelsPerUnit: JSONRat{new(big.Rat).SetInt64(1)}, } if err := json.Unmarshal(data, defaultConfig); err != nil { From 8e654d7ad60b68b609be069ebcdfd045978102f6 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Fri, 2 Aug 2024 11:56:28 +0200 Subject: [PATCH 157/203] fix: add AIModels currency config field and fix pixelsPerUnit handling This commit adds a new `currency` field to the `AIModelConfig` to specify the currency. Additionally, it improves the AI startup code in `starter.go` to correctly handle parsing of this currency while ensuring compatibility with 'offchain' mode. Further improvements to the AI startup code are deferred to avoid conflicts with existing pull requests. --- cmd/livepeer/starter/starter.go | 82 +++++++++++++++++++++------------ core/ai.go | 18 +------- 2 files changed, 53 insertions(+), 47 deletions(-) diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index bd95626b8..5354820ab 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -1095,6 +1095,16 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { return } + // Get base pixels per unit. + pixelsPerUnitBase, ok := new(big.Rat).SetString(*cfg.PixelsPerUnit) + if !ok || !pixelsPerUnitBase.IsInt() { + panic(fmt.Errorf("-pixelsPerUnit must be a valid integer, provided %v", *cfg.PixelsPerUnit)) + } + if !ok || pixelsPerUnitBase.Sign() <= 0 { + // Can't divide by 0 + panic(fmt.Errorf("-pixelsPerUnit must be > 0, provided %v", *cfg.PixelsPerUnit)) + } + if *cfg.AIModels != "" { configs, err := core.ParseAIModelConfigs(*cfg.AIModels) if err != nil { @@ -1105,30 +1115,28 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { for _, config := range configs { modelConstraint := &core.ModelConstraint{Warm: config.Warm} - // Set price per unit base info. - pixelsPerUnit, ok := new(big.Rat).SetString(*cfg.PixelsPerUnit) - if !ok || !pixelsPerUnit.IsInt() { - panic(fmt.Errorf("-pixelsPerUnit must be a valid integer, provided %v", *cfg.PixelsPerUnit)) - } - if pixelsPerUnit.Sign() <= 0 { - // Can't divide by 0 - panic(fmt.Errorf("-pixelsPerUnit must be > 0, provided %v", *cfg.PixelsPerUnit)) - } - pricePerUnit, currency, err := parsePricePerUnit(config.PricePerUnit.String()) - if err != nil { - panic(fmt.Errorf("'pricePerUnit' value specified for model '%v' in pipeline '%v' must be a valid integer with an optional currency, provided %v", config.ModelID, config.Pipeline, config.PricePerUnit)) - } else if pricePerUnit.Sign() < 0 { - panic(fmt.Errorf("'pricePerUnit' value specified for model '%v' in pipeline '%v' must be >= 0, provided %v", config.ModelID, config.Pipeline, config.PricePerUnit)) - } - pricePerPixel := new(big.Rat).Quo(pricePerUnit, pixelsPerUnit) var autoPrice *core.AutoConvertedPrice - if *cfg.Network == "offchain" { - autoPrice = core.NewFixedPrice(pricePerPixel) - } else { - autoPrice, err = core.NewAutoConvertedPrice(currency, pricePerPixel, nil) - } - if err != nil { - panic(fmt.Errorf("error converting price: %v", err)) + if *cfg.Network != "offchain" { + pixelsPerUnit := config.PixelsPerUnit.Rat + if config.PixelsPerUnit.Rat == nil { + pixelsPerUnit = pixelsPerUnitBase + } else { + if !pixelsPerUnit.IsInt() || pixelsPerUnit.Sign() <= 0 { + panic(fmt.Errorf("'pixelsPerUnit' value specified for model '%v' in pipeline '%v' must be a valid positive integer, provided %v", config.ModelID, config.Pipeline, config.PixelsPerUnit)) + } + } + pricePerUnit := config.PricePerUnit.Rat + if err != nil { + panic(fmt.Errorf("'pricePerUnit' value specified for model '%v' in pipeline '%v' must be a valid integer with an optional currency, provided %v", config.ModelID, config.Pipeline, config.PricePerUnit)) + } else if pricePerUnit.Sign() < 0 { + panic(fmt.Errorf("'pricePerUnit' value specified for model '%v' in pipeline '%v' must be >= 0, provided %v", config.ModelID, config.Pipeline, config.PricePerUnit)) + } + pricePerPixel := new(big.Rat).Quo(pricePerUnit, pixelsPerUnit) + + autoPrice, err = core.NewAutoConvertedPrice(config.Currency, pricePerPixel, nil) + if err != nil { + panic(fmt.Errorf("error converting price: %v", err)) + } } // If the config contains a URL we call Warm() anyway because AIWorker will just register @@ -1158,7 +1166,9 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { capabilityConstraints[core.Capability_TextToImage].Models[config.ModelID] = modelConstraint - n.SetBasePriceForCap("default", core.Capability_TextToImage, config.ModelID, autoPrice) + if *cfg.Network != "offchain" { + n.SetBasePriceForCap("default", core.Capability_TextToImage, config.ModelID, autoPrice) + } case "image-to-image": _, ok := capabilityConstraints[core.Capability_ImageToImage] if !ok { @@ -1170,7 +1180,9 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { capabilityConstraints[core.Capability_ImageToImage].Models[config.ModelID] = modelConstraint - n.SetBasePriceForCap("default", core.Capability_ImageToImage, config.ModelID, autoPrice) + if *cfg.Network != "offchain" { + n.SetBasePriceForCap("default", core.Capability_ImageToImage, config.ModelID, autoPrice) + } case "image-to-video": _, ok := capabilityConstraints[core.Capability_ImageToVideo] if !ok { @@ -1182,7 +1194,9 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { capabilityConstraints[core.Capability_ImageToVideo].Models[config.ModelID] = modelConstraint - n.SetBasePriceForCap("default", core.Capability_ImageToVideo, config.ModelID, autoPrice) + if *cfg.Network != "offchain" { + n.SetBasePriceForCap("default", core.Capability_ImageToVideo, config.ModelID, autoPrice) + } case "upscale": _, ok := capabilityConstraints[core.Capability_Upscale] if !ok { @@ -1194,7 +1208,9 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { capabilityConstraints[core.Capability_Upscale].Models[config.ModelID] = modelConstraint - n.SetBasePriceForCap("default", core.Capability_Upscale, config.ModelID, autoPrice) + if *cfg.Network != "offchain" { + n.SetBasePriceForCap("default", core.Capability_Upscale, config.ModelID, autoPrice) + } case "audio-to-text": _, ok := capabilityConstraints[core.Capability_AudioToText] if !ok { @@ -1206,14 +1222,20 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { capabilityConstraints[core.Capability_AudioToText].Models[config.ModelID] = modelConstraint - n.SetBasePriceForCap("default", core.Capability_AudioToText, config.ModelID, autoPrice) + if *cfg.Network != "offchain" { + n.SetBasePriceForCap("default", core.Capability_AudioToText, config.ModelID, autoPrice) + } } if len(aiCaps) > 0 { capability := aiCaps[len(aiCaps)-1] price := n.GetBasePriceForCap("default", capability, config.ModelID) - pricePerUnit := price.Num().Int64() / price.Denom().Int64() - glog.V(6).Infof("Capability %s (ID: %v) advertised with model constraint %s at price %d wei per compute unit", config.Pipeline, capability, config.ModelID, pricePerUnit) + if *cfg.Network != "offchain" { + pricePerUnit := price.Num().Int64() / price.Denom().Int64() + glog.V(6).Infof("Capability %s (ID: %v) advertised with model constraint %s at price %d wei per compute unit", config.Pipeline, capability, config.ModelID, pricePerUnit) + } else { + glog.V(6).Infof("Capability %s (ID: %v) advertised with model constraint %s", config.Pipeline, capability, config.ModelID) + } } } } else { diff --git a/core/ai.go b/core/ai.go index 5f8a1694a..9ac86a307 100644 --- a/core/ai.go +++ b/core/ai.go @@ -49,26 +49,10 @@ type AIModelConfig struct { Warm bool `json:"warm,omitempty"` PricePerUnit JSONRat `json:"price_per_unit,omitempty"` PixelsPerUnit JSONRat `json:"pixels_per_unit,omitempty"` + Currency string `json:"currency,omitempty"` OptimizationFlags worker.OptimizationFlags `json:"optimization_flags,omitempty"` } -func (config *AIModelConfig) UnmarshalJSON(data []byte) error { - // Custom type to avoid recursive calls to UnmarshalJSON - type AIModelConfigAlias AIModelConfig - // Set default values for fields - defaultConfig := &AIModelConfigAlias{ - PixelsPerUnit: JSONRat{new(big.Rat).SetInt64(1)}, - } - - if err := json.Unmarshal(data, defaultConfig); err != nil { - return err - } - - *config = AIModelConfig(*defaultConfig) - - return nil -} - func ParseAIModelConfigs(config string) ([]AIModelConfig, error) { var configs []AIModelConfig From 3ff8e74f134a83e94d135bf9613e15785fbe0731 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Fri, 2 Aug 2024 17:47:01 +0200 Subject: [PATCH 158/203] fix(ai): temporarily disable arm64 builds This commit disables the linux/arm64 for now as we haven't yet added support it. This prevent the Docker CI from failing. --- .github/workflows/docker.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 51be5052e..5da5df2d1 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -90,8 +90,8 @@ jobs: build-args: | BUILD_TAGS=${{ steps.build-tag.outputs.build-tags }} context: . - platforms: linux/amd64, linux/arm64 - # platforms: linux/amd64 + # platforms: linux/amd64, linux/arm64 # NOTE: Arm64 not yet supported. + platforms: linux/amd64 push: true tags: ${{ steps.meta.outputs.tags }} file: "docker/Dockerfile" @@ -179,8 +179,8 @@ jobs: build-args: | BUILD_TAGS=${{ steps.build-tag.outputs.build-tags }} context: . - platforms: linux/amd64, linux/arm64 - # platforms: linux/amd64 + # platforms: linux/amd64, linux/arm64 # NOTE: Arm64 not yet supported. + platforms: linux/amd64 push: true tags: ${{ steps.meta-builder.outputs.tags }} file: "docker/Dockerfile" From 9cdeeb5a22674e78bd5593f22604858ed6c2feb2 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Fri, 2 Aug 2024 17:47:01 +0200 Subject: [PATCH 159/203] fix(ai): temporarily disable arm64 builds This commit disables the linux/arm64 for now as we haven't yet added support it. This prevent the Docker CI from failing. --- .github/workflows/docker.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 51be5052e..5da5df2d1 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -90,8 +90,8 @@ jobs: build-args: | BUILD_TAGS=${{ steps.build-tag.outputs.build-tags }} context: . - platforms: linux/amd64, linux/arm64 - # platforms: linux/amd64 + # platforms: linux/amd64, linux/arm64 # NOTE: Arm64 not yet supported. + platforms: linux/amd64 push: true tags: ${{ steps.meta.outputs.tags }} file: "docker/Dockerfile" @@ -179,8 +179,8 @@ jobs: build-args: | BUILD_TAGS=${{ steps.build-tag.outputs.build-tags }} context: . - platforms: linux/amd64, linux/arm64 - # platforms: linux/amd64 + # platforms: linux/amd64, linux/arm64 # NOTE: Arm64 not yet supported. + platforms: linux/amd64 push: true tags: ${{ steps.meta-builder.outputs.tags }} file: "docker/Dockerfile" From c2da0bd8bbd70361cd676aacbe08a3b5f6097801 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Wed, 7 Aug 2024 22:53:11 +0200 Subject: [PATCH 160/203] chore: merge master into ai-video (#3103) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * eth,eth/watcher: Create Chainlink price feed watcher (#2972) * eth/watchers: Create PriceFeed watcher Makefile: Use mockgen binary from tool dependencies eth/contracts: Add chainlink interfaces source Makefile: Generate Chainlink contracts ABI tools: Add abigen tool to repo eth/contracts: Generate chainlink bindings Makefile: Fix abigen bindings generation Revert everything abigen Turns out there's already bindings exported from the Chainlink lib. go.mod: Add chainlink library eth/watchers: Add pricefeed watcher eth/watchers: Clean-up event watching code eth/watchers: Improve price tracking Revert "go.mod: Add chainlink library" This reverts commit ac415bd8fb210088874e7fdea8d37ac4dad81dab. Revert "Revert everything abigen" This reverts commit b7c40b1e936c885aad973f28d87d42b0d85cb0e4. eth/contracts: Gen bindings for proxy iface eth/watchers: Use local bindings for contracts eth/watchers: Simplify event subs logic eth/watchers: Simplify&optimize truncated ticker eth/watchers: Update decimals on fetch eth/watchers: Improve handling of decimals eth/watchers: Fix price rat creation eth/watchers: Make sure we use UTC on truncated timer eth/contracts/chainlink: Generate only V3 contract bindings eth/watchers: Watch PriceFeed only with polling eth/watchers: Add a retry logic on price update eth/watchers: Use clog instead of fmt.Printf * eth: Create separate pricefeed client unit This will make the code more testable. * eth: Add tests for pricefeed client * eth/watchers: Add tests to the truncated ticker Gosh that was much harder than I thought * eth/watchers: Add tests for pricefeedwatcher * eth: Add comments to the new components * go fmt * eth: Address minor review comments * eth,eth/watchers: Improve pricefeed watcher interface * eth/watchers: Remove truncated ticker tests * cmd/livepeer: Use price feed watcher for dynamic pricePerPixel (#2981) * eth/watchers: Create PriceFeed watcher Makefile: Use mockgen binary from tool dependencies eth/contracts: Add chainlink interfaces source Makefile: Generate Chainlink contracts ABI tools: Add abigen tool to repo eth/contracts: Generate chainlink bindings Makefile: Fix abigen bindings generation Revert everything abigen Turns out there's already bindings exported from the Chainlink lib. go.mod: Add chainlink library eth/watchers: Add pricefeed watcher eth/watchers: Clean-up event watching code eth/watchers: Improve price tracking Revert "go.mod: Add chainlink library" This reverts commit ac415bd8fb210088874e7fdea8d37ac4dad81dab. Revert "Revert everything abigen" This reverts commit b7c40b1e936c885aad973f28d87d42b0d85cb0e4. eth/contracts: Gen bindings for proxy iface eth/watchers: Use local bindings for contracts eth/watchers: Simplify event subs logic eth/watchers: Simplify&optimize truncated ticker eth/watchers: Update decimals on fetch eth/watchers: Improve handling of decimals eth/watchers: Fix price rat creation eth/watchers: Make sure we use UTC on truncated timer eth/contracts/chainlink: Generate only V3 contract bindings eth/watchers: Watch PriceFeed only with polling eth/watchers: Add a retry logic on price update eth/watchers: Use clog instead of fmt.Printf * eth: Create separate pricefeed client unit This will make the code more testable. * eth: Add tests for pricefeed client * eth/watchers: Add tests to the truncated ticker Gosh that was much harder than I thought * eth/watchers: Add tests for pricefeedwatcher * eth: Add comments to the new components * go fmt * cmd: make pricePerUnit flags strings * cmd: Allow price per unit to be speficied with a currency Currently ignoring the currency value. * cmd: Add logic to start price update loop * cmd: Add flag for specifying price feed address * cmd: Add a lil test to priceDataToWei * TODO: Reminder for something I noticed is missing * cmd/starter: Support currencies for custom broadcaster prices * eth: Address minor review comments * eth,eth/watchers: Improve pricefeed watcher interface * eth/watchers: Fix pricefeed watcher after merge * cmd,core,server: Support dynamic updates to price in USD * eth/watchers: Remove truncated ticker tests * eth/watchers: Finalize pricefeedwatcher docs/tests * cmd: Address review comment * core: Create tests for autoconvertedprice * cmd,core: Move wei default to AutoConvertedPrice * Address review comments * cmd: Fix the e2e flow for setting/updating configs * CHANGELOG * cmd: Make sure pricePerPixel can be specified with e notation Parse it directlty as a big.Rat from a raw string, like I was doing for pricePerUnit in some places. * Fix tests Turns out tests were not running on my branch due to base branch * go fmt * core: Fix typo in comment * cmd,server: Use 3 decimal points when logging PPP Found out that's officially supported precision on the discovery logic, so let's reflect that here. * Release 0.7.3 (#2988) * release v0.7.3 * release v0.7.3 * Revert "Bump ffmpeg version and nv-codec-headers" (#2989) * Revert "Bump ffmpeg version and nv-codec-headers (#2973)" This reverts commit cad6713174fc351850ae5a2f82b0f6ab306fc133. * Revert "Update CUDA build version from 11.7.1 to 12.0.0 (#2978)" This reverts commit 6c09a9f00682d7ed404257bba9f485dc0c4041ad. * Reapply "Update CUDA build version from 11.7.1 to 12.0.0 (#2978)" This reverts commit ebbf2102b8f6420a02439554fbe4992ab1113749. * Force ffmpeg reinstall * Revert "Force ffmpeg reinstall" This reverts commit 5adb9a598b14b3d12f038201d7d8374fac5e2f8c. --------- Co-authored-by: Victor Elias * Bump LPMS (#2992) * release v0.7.4 (#2993) * server: Skip redundant maxPrice check in ongoing session (#2994) * server: Remove maxPrice check mid-session * server: Fix tests * server: Fix erroneous usage of assert.EqualErrorf When I was writing the tests for validatePrice I found out we were using that function wrong in a couple places and never checking any error. We were sending err.Error() to check the error from err. * server: Fix error checks after fixing assertion * CHANGELOG * server: Allow Os price to increase up to 2x mid-session (#2995) * server: Allow dynamic (and sometimes >max) prices for Os * CHANGELOG * server,discovery: Allow B to use any O in case none match maxPrice (#2999) * discovery: Ignore maxPrice on db_discovery queries Still kept the feature on the db as it had all the tests already implemented and could still be useful in the future. I can remove it if preferred though. * server: Get prices as big rats for selection While this may not seem useful now since we just convert them to floats on the probability calculation, it will be useful later when comparing prices to max price. * server: Add maxPrice filter logic on selection algorithm * CHANGELOG * server: Break filter in 2 functions * Fix transcoding price metrics (#3001) * ci(ai): add AI issue templates This commit introduces two new AI-specific issue templates, aiming to streamline the routing of AI subnet-related issues and feature requests to the appropriate team. * ci(ai): add AI pull request labeler This commit adds a pull request labeler action that automatically attaches the `ai` label when a pull request is created to the `ai-video` branch. * ci: change issue template order and add PR labeler config (#3006) * ci: change issue template order This commit ensures that the main branch issue templates are put above the AI related issue templates. * ci(ai): add PR labeler config file This commmit adds a https://github.com/actions/labeler configuration file so that all PRs on the `ai-video` branch will be correctly labeled with the `ai` label. * ci(ai): fix incorrect labels (#3012) * ci(ai): fix incorrect labels This commit fixed the labels that were specified in the Issue Templates to the one found in the repository. * ci: rename labeler and remove trailing whitespace * ci(ai): fix pull request config warning (#3018) (#3019) * ci(ai): fix pull request config warning (#3018) This commit gets rid of the Pull request labeler configuration file warning. * ci(ai): auto assign AI issues This commit auto assigns the AI issues to the right member of the AI team. * ci(ai): cleanup labeler actions This commit cleans up the labeler actions and ensure they run on the right triggers. * Initialize round by any B/O who has the initializeRound flag set to true (#3029) * Fix CI Darwin Build (#3049) * Fix CI * Fix CI * chore: fix function names (#3040) Signed-off-by: kevincatty Co-authored-by: Rafał Leszko * Create option to filter Os by min livepeer version used (#3050) * Update LPMS with the mobile transcoding fix (#3003) * Release 0.7.5 (#3051) * refactor: add -gateway and deprecate -broadcaster (#3053) * refactor: add -gateway and deprecate -broadcaster This commit adds the `gateway` flag and deprecates the `broadcaster` flag per core team decision (details: https://discord.com/channels/423160867534929930/1051963444598943784/1210356864643109004). * chore: update pending changelog --------- Co-authored-by: John | Elite Encoder * refactor(census): rename Broadcaster metrics to Gateway (#3055) * refactor(census): rename Broadcaster metrics to Gateway This commit renames the metrics related to Broadcaster to Gateway, following a team decision. More details can be found in the discussion here: [Team Discussion Link](.com/channels/423160867534929930/1051963444598943784/1210356864643109004). * chore: update pending changelog * refactor: add -pricePerGateway and deprecate -pricePerBroadcaster (#3056) * refactor: add -pricePerGateway and deprecate -pricePerBroadcaster This commit adds the `pricePerGateway` flag and deprecates the `pricePerBroadcaster` flag per core team decision (details: https://discord.com/channels/423160867534929930/1051963444598943784/1210356864643109004). * chore: update pending changelog * refactor: remove redundant deprecation comment This commit removes the `PricePerBroadcaster` deprecation comment since this is already clear from the glog warning below. * fix: correct the `pricePerBroadcaster` flag check This commit ensures that the deprecation condition for the `pricePerBroadcaster` flag properly handles the default empty string value. * fix: ensure 'pricePerGateway' is used This commit ensures that the `pricePerGateway` is correctly used instead of the `pricePerBroadcaster` when it is set. * refactor: deprecate 'pricePerGateway' broadcasters property in favor of 'gateways' This commit updates the configuration to replace the `broadcasters` property specified under the `pricePerGateway` flag with `gateways`. Additionally, it ensures that a warning is issued when the deprecated property is still used. * test: fix TestParseGetBroadcasterPrices test This commit ensures that the TestParseGetBroadcasterPrices function uses the new getGatewayPrices function. * test: rename TestParseGetBroadcasterPrices to reflect Gateway naming This commit updates the `TestParseGetBroadcasterPrices` function to `TestParseGetGatewayPrices` to align with the new node naming convention. * ci: protect Docker 'stable' tag (#3062) This commit introduces a safeguard to ensure that the Docker image tagged as 'stable' is only pushed when a new tag is created on the stable branch. This prevents unintended updates to the stable Docker image, ensuring consistency and reliability for users relying on the stable tag. * Return appropriate errors when auth fails, not just 5xx (#3065) * Return appropriate errors when auth fails, not just 5xx * Fix unit tests for new signature * Fix remaining tests * Fix another test * Refactor Forbidden to error type * Refactor Forbidden to error type --------- Co-authored-by: Rafał Leszko * ci: fix syntax error in Docker action tags (#3068) * ci: fix syntax error in Docker action tags This commit addresses a syntax error in the Docker image tag creation step. * ci(docker): ensure stable tag is created on master branch This commit ensures that the stable tag is created on the master branch. * chore: fix some comments (#3070) Signed-off-by: linghuying <1599935829@qq.com> * Add logging to selection_algorithm.go (#3076) * Add logging to selection_algorithm.go * Add even more logging * Fix typo in logs (#3079) * Add ctx to logging for selection algorithm (#3080) * Add ctx to logging for selection algorithm * Reorg imports * Fix unit tests * chore: make function comment match function name (#3081) Signed-off-by: tongjicoder * refactor: rename internal references from Broadcaster to Gateway (#3060) * refactor: rename internal references from Broadcaster to Gateway This commit updates internal references from 'Broadcaster' to 'Gateway' in accordance with the core team’s decision. For more details, refer to the discussion: [Discord Link](https://discord.com/channels/423160867534929930/1051963444598943784/1210356864643109004). * chore: update pending changelog * Add logging to the session refresh (#3083) * Add `/healthz` endpoint (#3095) * Update LPMS to ffmpeg 7 (#3096) * install_ffmpeg: point to LPMS * Update to use ffmpeg7 LPMS * release v0.7.6 * chore(ai): ensure ai-video-rebased ffmpeg file is used Since the lpms `ai-video` and `ai-video-rebase-main` branches are not yet merged into the main branch we need to ensure the right AI install_ffmpeg.sh script is used. * chore(ai): remove local go module dependency This commit removes the local go module dependency to lpms that was accidentally commited. * test: fix broadcast test This commit fixes the bugs that were introduced by the AI codebas einto the broadcast test functions. * feat(ai): add dynamic pricePerUnit feature to AI pricing This commit ensures that Orchestrators can set their pricing in USD and the price gets updated dynamically. * cmd: Use `-gateway` consistently Avoid references to deprecated CLI flags. * feat(ai): add minLivepeerVersion constraint and IgnorePreReleaseVersions - Adds minLivepeerVersion constraint from https://github.com/livepeer/go-livepeer/pull/3050 to the AI codebase. - Introduces `IgnorePreReleaseVersions` flag to filter out pre-release versions. This update is essential for distinguishing AI subnet versions, which use pre-release suffixes, from standard transcoding releases. This new flag can be removed when merging in the main branch. * fix(ai): fix offchain 'PriceFeedWatcher is not initialized' error This commit ensures that the `PriceFeedWatcher is not initialized` error is not thrown when the software is run in `offchain` mode. * test: add pre-release suffix min version tests This commit adds some tests to check if pre-release versions are correctly handled when filtering orchestrators based on minimum version. * feat: simplify pre-release version filter logic This commit removes the POC `ignorePreReleaseVersions` command line argument for logic in the `LivepeerVersionCompatibleWith` which ensures that pre-release versions are only checked when the gateway specifies as pre-release suffix in the `orchMinLivepeerVersion` command line argument. * feat: allow Gateways to filter by pre-release This commit gives Gateways the ability to filter by pre-release suffix. When a pre-release suffix is specified in the `OrchMinLivepeerVersion` command line argument the software now also checks the pre-release version suffix on the orchestrator. * refactor(ai): rename capability constraint variable This commit renames the orchConstraints variable in the capabilities.go file to better reflect that it are constraints per capabilities. * chore(ai): remove lpms local dependency This commit ensure that the right remote dependency is used for the lpms package. * chore(ai): update lpms dependency This commit updates the lpms dependency to the lastest version of the `ai-video-rebase-main`. * feat: remove AI specific minVersion constraint logic This commit removes the ai-specific logic that allows ai suffixes to work during version constraint checking. This was done to add it back in a seperate commit so that we can always revert. * feat(ai): Add AI suffix handling for min version logic This commit introduces logic to handle version constraints with AI-specific suffixes (e.g., v0.7.6-ai.1). This ensures compatibility during version constraint filtering. Note that this logic should be removed when merging into the master branch. * core: Make the AI config take a big rat * fix: add AIModels currency config field and fix pixelsPerUnit handling This commit adds a new `currency` field to the `AIModelConfig` to specify the currency. Additionally, it improves the AI startup code in `starter.go` to correctly handle parsing of this currency while ensuring compatibility with 'offchain' mode. Further improvements to the AI startup code are deferred to avoid conflicts with existing pull requests. * fix(ai): temporarily disable arm64 builds This commit disables the linux/arm64 for now as we haven't yet added support it. This prevent the Docker CI from failing. --------- Signed-off-by: kevincatty Signed-off-by: linghuying <1599935829@qq.com> Signed-off-by: tongjicoder Co-authored-by: Victor Elias Co-authored-by: Rafał Leszko Co-authored-by: Thom Shutt Co-authored-by: Victor Elias Co-authored-by: kevincatty <168698033+kevincatty@users.noreply.github.com> Co-authored-by: John | Elite Encoder Co-authored-by: linghuying <1599935829@qq.com> Co-authored-by: tongjicoder Co-authored-by: Josh Allmann --- .github/workflows/build.yaml | 2 +- .gitignore | 3 + CHANGELOG.md | 130 +- Makefile | 12 +- VERSION | 2 +- cmd/devtool/devtool.go | 2 +- cmd/livepeer/livepeer.go | 15 +- cmd/livepeer/starter/starter.go | 705 ++-- cmd/livepeer/starter/starter_test.go | 92 +- cmd/livepeer_cli/wizard.go | 2 +- cmd/livepeer_cli/wizard_broadcast.go | 22 +- cmd/livepeer_cli/wizard_stats.go | 34 +- cmd/livepeer_cli/wizard_transcoder.go | 49 +- common/types.go | 2 +- core/ai.go | 40 +- core/autoconvertedprice.go | 139 + core/autoconvertedprice_test.go | 258 ++ core/capabilities.go | 116 +- core/capabilities_test.go | 187 + core/core_test.go | 4 +- core/livepeernode.go | 40 +- core/livepeernode_test.go | 38 +- core/orch_test.go | 63 +- core/orchestrator.go | 8 +- ..._RoundTrip_Net-20240729130524-3824236.fail | 828 +++++ core/transcoder_test.go | 4 +- discovery/db_discovery.go | 14 +- discovery/discovery_test.go | 34 +- .../chainlink/AggregatorV3Interface.abi | 1 + .../chainlink/AggregatorV3Interface.go | 394 +++ .../chainlink/AggregatorV3Interface.sol | 36 + eth/pricefeed.go | 78 + eth/pricefeed_test.go | 51 + eth/roundinitializer.go | 95 +- eth/roundinitializer_test.go | 112 +- eth/watchers/pricefeedwatcher.go | 234 ++ eth/watchers/pricefeedwatcher_test.go | 249 ++ go.mod | 56 +- go.sum | 224 +- install_ffmpeg.sh | 231 +- monitor/census.go | 26 +- net/lp_rpc.pb.go | 3143 +++++++---------- net/lp_rpc.proto | 13 +- net/lp_rpc_grpc.pb.go | 122 +- net/redeemer.pb.go | 1 - pm/recipient.go | 2 +- pm/sender.go | 2 +- pm/stub.go | 2 +- server/ai_session.go | 7 +- server/broadcast.go | 22 +- server/handlers.go | 109 +- server/handlers_test.go | 35 +- server/mediaserver.go | 65 +- server/mediaserver_test.go | 118 +- server/push_test.go | 2 +- server/rpc.go | 3 +- server/rpc_test.go | 35 +- server/segment_rpc.go | 20 +- server/segment_rpc_test.go | 9 +- server/selection.go | 9 +- server/selection_algorithm.go | 51 +- server/selection_algorithm_test.go | 122 +- server/selection_test.go | 8 +- test/e2e/e2e.go | 2 +- tools.go | 9 + 65 files changed, 5404 insertions(+), 3139 deletions(-) create mode 100644 core/autoconvertedprice.go create mode 100644 core/autoconvertedprice_test.go create mode 100644 core/testdata/rapid/TestCapability_RoundTrip_Net/TestCapability_RoundTrip_Net-20240729130524-3824236.fail create mode 100644 eth/contracts/chainlink/AggregatorV3Interface.abi create mode 100644 eth/contracts/chainlink/AggregatorV3Interface.go create mode 100644 eth/contracts/chainlink/AggregatorV3Interface.sol create mode 100644 eth/pricefeed.go create mode 100644 eth/pricefeed_test.go create mode 100644 eth/watchers/pricefeedwatcher.go create mode 100644 eth/watchers/pricefeedwatcher_test.go create mode 100644 tools.go diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 022d397b8..b7b8b5609 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -158,7 +158,7 @@ jobs: target: - GOOS: darwin GOARCH: amd64 - runner: macos-latest + runner: macos-14-large - GOOS: darwin GOARCH: arm64 diff --git a/.gitignore b/.gitignore index ec5f509ad..9d493d245 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,10 @@ *.dll *.so *.dylib + +# IDE files *.vscode +*.code-workspace # Test binary, build with `go test -c` *.test diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bebd6eff..fb3c5d803 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,133 @@ # Changelog +## v0.7.6 + +- [#3055](https://github.com/livepeer/go-livepeer/pull/3055) census: Rename broadcaster metrics to gateway metrics +- [#3053](https://github.com/livepeer/go-livepeer/pull/3053) cli: add `-gateway` flag and deprecate `-broadcaster` flag. +- [#3056](https://github.com/livepeer/go-livepeer/pull/3056) cli: add `-pricePerGateway` flag and deprecate `-pricePerBroadcaster` flag. +- [#3060](https://github.com/livepeer/go-livepeer/pull/3060) refactor: rename internal references from Broadcaster to Gateway + +### Breaking Changes 🚨🚨 + +### Features ⚒ + +#### General + +#### Broadcaster + +#### Orchestrator + +#### Transcoder + +### Bug Fixes 🐞 + +#### CLI + +#### General + +#### Broadcaster + +#### Orchestrator + +#### Transcoder + +## v0.7.5 + +### Breaking Changes 🚨🚨 + +### Features ⚒ + +#### General + +- [#3050](https://github.com/livepeer/go-livepeer/pull/3050) Create option to filter Os by min livepeer version used (@leszko) +- [#3029](https://github.com/livepeer/go-livepeer/pull/3029) Initialize round by any B/O who has the initializeRound flag set to true (@leszko) +- [#3040](https://github.com/livepeer/go-livepeer/pull/3040) Fix function names (@kevincatty) + +#### Broadcaster + +- [#2995](https://github.com/livepeer/go-livepeer/pull/2995) server: Allow Os price to increase up to 2x mid-session (@victorges) +- [#2999](https://github.com/livepeer/go-livepeer/pull/2999) server,discovery: Allow B to use any O in case none match maxPrice (@victorges) + +#### Orchestrator + +#### Transcoder + +### Bug Fixes 🐞 + +#### CLI + +#### General + +#### Broadcaster + +- [#2994](https://github.com/livepeer/go-livepeer/pull/2994) server: Skip redundant maxPrice check in ongoing session (@victorges) + +#### Orchestrator + +- [#3001](https://github.com/livepeer/go-livepeer/pull/3001) Fix transcoding price metrics (@leszko) + +#### Transcoder + +- [#3003](https://github.com/livepeer/go-livepeer/pull/3003) Fix issue in the transcoding layer for WebRTC input (@j0sh) + +## v0.7.4 + +### Breaking Changes 🚨🚨 + +### Features ⚒ + +#### General + +- [#2989](https://github.com/livepeer/go-livepeer/pull/2989) Revert "Update ffmpeg version" (@thomshutt) + +#### Broadcaster + +#### Orchestrator + +#### Transcoder + +### Bug Fixes 🐞 + +#### CLI + +#### General + +#### Broadcaster + +#### Orchestrator + +#### Transcoder + +## v0.7.3 + +### Breaking Changes 🚨🚨 + +### Features ⚒ + +#### General + +- [#2978](https://github.com/livepeer/go-livepeer/pull/2978) Update CUDA version from 11.x to 12.x (@leszko) +- [#2973](https://github.com/livepeer/go-livepeer/pull/2973) Update ffmpeg version (@thomshutt) +- [#2981](https://github.com/livepeer/go-livepeer/pull/2981) Add support for prices in custom currencies like USD (@victorges) + +#### Broadcaster + +#### Orchestrator + +#### Transcoder + +### Bug Fixes 🐞 + +#### CLI + +#### General + +#### Broadcaster + +#### Orchestrator + +#### Transcoder + ## v0.7.2 ### Breaking Changes 🚨🚨 @@ -562,7 +690,7 @@ Additional highlights of this release: - Support for EIP-1559 (otherwise known as type 2) Ethereum transactions which results in more predictable transaction confirmation times, reduces the chance of stuck pending transactions and avoids overpaying in gas fees. If you are interested in additional details on the implications of EIP-1559 transactions refer to this [resource](https://hackmd.io/@timbeiko/1559-resources). - An improvement in ticket parameter generation for orchestrators to prevent short lived gas price spikes on the Ethereum network from disrupting streams. - The node will automatically detect if the GPU enters an unrecoverable state and crash. The reason for crashing upon detecting an unrecoverable GPU state is that no transcoding will -be possible in this scenario until the node is restarted. We recommend node operators to setup a process for monitoring if their node is still up and starting the node if it has crashed. For reference, a bash script similar to [this one](https://gist.github.com/jailuthra/03c3d65d0bbff457cae8f9a14b4c04b7) can be used to automate restarts of the node in the event of a crash. + be possible in this scenario until the node is restarted. We recommend node operators to setup a process for monitoring if their node is still up and starting the node if it has crashed. For reference, a bash script similar to [this one](https://gist.github.com/jailuthra/03c3d65d0bbff457cae8f9a14b4c04b7) can be used to automate restarts of the node in the event of a crash. Thanks to everyone that submitted bug reports and assisted in testing! diff --git a/Makefile b/Makefile index 54ca0c6e9..d57d7ffc9 100644 --- a/Makefile +++ b/Makefile @@ -2,8 +2,9 @@ SHELL=/bin/bash GO_BUILD_DIR?="./" MOCKGEN=go run github.com/golang/mock/mockgen +ABIGEN=go run github.com/ethereum/go-ethereum/cmd/abigen -all: net/lp_rpc.pb.go net/redeemer.pb.go net/redeemer_mock.pb.go core/test_segment.go livepeer livepeer_cli livepeer_router livepeer_bench +all: net/lp_rpc.pb.go net/redeemer.pb.go net/redeemer_mock.pb.go core/test_segment.go eth/contracts/chainlink/AggregatorV3Interface.go livepeer livepeer_cli livepeer_router livepeer_bench net/lp_rpc.pb.go: net/lp_rpc.proto protoc -I=. --go_out=. --go-grpc_out=. $^ @@ -18,6 +19,15 @@ net/redeemer_mock.pb.go net/redeemer_grpc_mock.pb.go: net/redeemer.pb.go net/red core/test_segment.go: core/test_segment.sh core/test_segment.go +eth/contracts/chainlink/AggregatorV3Interface.go: + solc --version | grep 0.7.6+commit.7338295f + @set -ex; \ + for sol_file in eth/contracts/chainlink/*.sol; do \ + contract_name=$$(basename "$$sol_file" .sol); \ + solc --abi --optimize --overwrite -o $$(dirname "$$sol_file") $$sol_file; \ + $(ABIGEN) --abi=$${sol_file%.sol}.abi --pkg=chainlink --type=$$contract_name --out=$${sol_file%.sol}.go; \ + done + version=$(shell cat VERSION) ldflags := -X github.com/livepeer/go-livepeer/core.LivepeerVersion=$(shell ./print_version.sh) diff --git a/VERSION b/VERSION index d5cc44d1d..4d01880a7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.7.2 \ No newline at end of file +0.7.6 \ No newline at end of file diff --git a/cmd/devtool/devtool.go b/cmd/devtool/devtool.go index 7052ed032..61e9774fe 100644 --- a/cmd/devtool/devtool.go +++ b/cmd/devtool/devtool.go @@ -95,7 +95,7 @@ func main() { if !goodToGo { fmt.Println(` Usage: go run cmd/devtool/devtool.go setup broadcaster|transcoder [nodeIndex] - It will create initilize eth account (on private testnet) to be used for broadcaster or transcoder + It will create initialize eth account (on private testnet) to be used for broadcaster or transcoder and will create shell script (run_broadcaster_ETHACC.sh or run_transcoder_ETHACC.sh) to run it. Node index indicates how much to offset node's port. Orchestrator node's index by default is 1. For example: diff --git a/cmd/livepeer/livepeer.go b/cmd/livepeer/livepeer.go index 99c625985..2653e55a6 100755 --- a/cmd/livepeer/livepeer.go +++ b/cmd/livepeer/livepeer.go @@ -127,13 +127,14 @@ func parseLivepeerConfig() starter.LivepeerConfig { cfg.OrchAddr = flag.String("orchAddr", *cfg.OrchAddr, "Comma-separated list of orchestrators to connect to") cfg.OrchWebhookURL = flag.String("orchWebhookUrl", *cfg.OrchWebhookURL, "Orchestrator discovery callback URL") cfg.OrchBlacklist = flag.String("orchBlocklist", "", "Comma-separated list of blocklisted orchestrators") + cfg.OrchMinLivepeerVersion = flag.String("orchMinLivepeerVersion", *cfg.OrchMinLivepeerVersion, "Minimal go-livepeer version orchestrator should have to be selected") cfg.SelectRandWeight = flag.Float64("selectRandFreq", *cfg.SelectRandWeight, "Weight of the random factor in the orchestrator selection algorithm") cfg.SelectStakeWeight = flag.Float64("selectStakeWeight", *cfg.SelectStakeWeight, "Weight of the stake factor in the orchestrator selection algorithm") cfg.SelectPriceWeight = flag.Float64("selectPriceWeight", *cfg.SelectPriceWeight, "Weight of the price factor in the orchestrator selection algorithm") cfg.SelectPriceExpFactor = flag.Float64("selectPriceExpFactor", *cfg.SelectPriceExpFactor, "Expresses how significant a small change of price is for the selection algorithm; default 100") cfg.OrchPerfStatsURL = flag.String("orchPerfStatsUrl", *cfg.OrchPerfStatsURL, "URL of Orchestrator Performance Stream Tester") cfg.Region = flag.String("region", *cfg.Region, "Region in which a broadcaster is deployed; used to select the region while using the orchestrator's performance stats") - cfg.MaxPricePerUnit = flag.Int("maxPricePerUnit", *cfg.MaxPricePerUnit, "The maximum transcoding price (in wei) per 'pixelsPerUnit' a broadcaster is willing to accept. If not set explicitly, broadcaster is willing to accept ANY price") + cfg.MaxPricePerUnit = flag.String("maxPricePerUnit", *cfg.MaxPricePerUnit, "The maximum transcoding price per 'pixelsPerUnit' a broadcaster is willing to accept. If not set explicitly, broadcaster is willing to accept ANY price. Can be specified in wei or a custom currency in the format (e.g. 0.50USD). When using a custom currency, a corresponding price feed must be configured with -priceFeedAddr") cfg.MinPerfScore = flag.Float64("minPerfScore", *cfg.MinPerfScore, "The minimum orchestrator's performance score a broadcaster is willing to accept") // Transcoding: @@ -169,6 +170,7 @@ func parseLivepeerConfig() starter.LivepeerConfig { cfg.MaxGasPrice = flag.Int("maxGasPrice", *cfg.MaxGasPrice, "Maximum gas price (priority fee + base fee) for ETH transactions in wei, 40 Gwei = 40000000000") cfg.EthController = flag.String("ethController", *cfg.EthController, "Protocol smart contract address") cfg.InitializeRound = flag.Bool("initializeRound", *cfg.InitializeRound, "Set to true if running as a transcoder and the node should automatically initialize new rounds") + cfg.InitializeRoundMaxDelay = flag.Duration("initializeRoundMaxDelay", *cfg.InitializeRoundMaxDelay, "Maximum delay to wait before initializing a round") cfg.TicketEV = flag.String("ticketEV", *cfg.TicketEV, "The expected value for PM tickets") cfg.MaxFaceValue = flag.String("maxFaceValue", *cfg.MaxFaceValue, "set max ticket face value in WEI") // Broadcaster max acceptable ticket EV @@ -178,12 +180,13 @@ func parseLivepeerConfig() starter.LivepeerConfig { // Broadcaster deposit multiplier to determine max acceptable ticket faceValue cfg.DepositMultiplier = flag.Int("depositMultiplier", *cfg.DepositMultiplier, "The deposit multiplier used to determine max acceptable faceValue for PM tickets") // Orchestrator base pricing info - cfg.PricePerUnit = flag.Int("pricePerUnit", 0, "The price per 'pixelsPerUnit' amount pixels") - // Unit of pixels for both O's basePriceInfo and B's MaxBroadcastPrice - cfg.PixelsPerUnit = flag.Int("pixelsPerUnit", *cfg.PixelsPerUnit, "Amount of pixels per unit. Set to '> 1' to have smaller price granularity than 1 wei / pixel") + cfg.PricePerUnit = flag.String("pricePerUnit", "0", "The price per 'pixelsPerUnit' amount pixels. Can be specified in wei or a custom currency in the format (e.g. 0.50USD). When using a custom currency, a corresponding price feed must be configured with -priceFeedAddr") + // Unit of pixels for both O's pricePerUnit and B's maxPricePerUnit + cfg.PixelsPerUnit = flag.String("pixelsPerUnit", *cfg.PixelsPerUnit, "Amount of pixels per unit. Set to '> 1' to have smaller price granularity than 1 wei / pixel") + cfg.PriceFeedAddr = flag.String("priceFeedAddr", *cfg.PriceFeedAddr, "ETH address of the Chainlink price feed contract. Used for custom currencies conversion on -pricePerUnit or -maxPricePerUnit") cfg.AutoAdjustPrice = flag.Bool("autoAdjustPrice", *cfg.AutoAdjustPrice, "Enable/disable automatic price adjustments based on the overhead for redeeming tickets") - cfg.PricePerGateway = flag.String("pricePerGateway", *cfg.PricePerGateway, `json list of price per gateway or path to json config file. Example: {"gateways":[{"ethaddress":"address1","priceperunit":1000,"pixelsperunit":1},{"ethaddress":"address2","priceperunit":1200,"pixelsperunit":1}]}`) - cfg.PricePerBroadcaster = flag.String("pricePerBroadcaster", *cfg.PricePerBroadcaster, `json list of price per broadcaster or path to json config file. Example: {"broadcasters":[{"ethaddress":"address1","priceperunit":1000,"pixelsperunit":1},{"ethaddress":"address2","priceperunit":1200,"pixelsperunit":1}]}`) + cfg.PricePerGateway = flag.String("pricePerGateway", *cfg.PricePerGateway, `json list of price per gateway or path to json config file. Example: {"gateways":[{"ethaddress":"address1","priceperunit":0.5,"currency":"USD","pixelsperunit":1000000000000},{"ethaddress":"address2","priceperunit":0.3,"currency":"USD","pixelsperunit":1000000000000}]}`) + cfg.PricePerBroadcaster = flag.String("pricePerBroadcaster", *cfg.PricePerBroadcaster, `json list of price per broadcaster or path to json config file. Example: {"broadcasters":[{"ethaddress":"address1","priceperunit":0.5,"currency":"USD","pixelsperunit":1000000000000},{"ethaddress":"address2","priceperunit":0.3,"currency":"USD","pixelsperunit":1000000000000}]}`) // Interval to poll for blocks cfg.BlockPollingInterval = flag.Int("blockPollingInterval", *cfg.BlockPollingInterval, "Interval in seconds at which different blockchain event services poll for blocks") // Redemption service diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index 7568f1f13..5354820ab 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -16,6 +16,7 @@ import ( "os/user" "path" "path/filepath" + "regexp" "strconv" "strings" "time" @@ -34,6 +35,7 @@ import ( "github.com/livepeer/go-livepeer/eth" "github.com/livepeer/go-livepeer/eth/blockwatch" "github.com/livepeer/go-livepeer/eth/watchers" + "github.com/livepeer/go-livepeer/monitor" lpmon "github.com/livepeer/go-livepeer/monitor" "github.com/livepeer/go-livepeer/pm" "github.com/livepeer/go-livepeer/server" @@ -76,81 +78,84 @@ const ( ) type LivepeerConfig struct { - Network *string - RtmpAddr *string - CliAddr *string - HttpAddr *string - ServiceAddr *string - OrchAddr *string - VerifierURL *string - EthController *string - VerifierPath *string - LocalVerify *bool - HttpIngest *bool - Orchestrator *bool - Transcoder *bool - AIWorker *bool - Gateway *bool - Broadcaster *bool - OrchSecret *string - TranscodingOptions *string - AIModels *string - MaxAttempts *int - SelectRandWeight *float64 - SelectStakeWeight *float64 - SelectPriceWeight *float64 - SelectPriceExpFactor *float64 - OrchPerfStatsURL *string - Region *string - MaxPricePerUnit *int - MinPerfScore *float64 - MaxSessions *string - CurrentManifest *bool - Nvidia *string - Netint *string - TestTranscoder *bool - EthAcctAddr *string - EthPassword *string - EthKeystorePath *string - EthOrchAddr *string - EthUrl *string - TxTimeout *time.Duration - MaxTxReplacements *int - GasLimit *int - MinGasPrice *int64 - MaxGasPrice *int - InitializeRound *bool - TicketEV *string - MaxFaceValue *string - MaxTicketEV *string - MaxTotalEV *string - DepositMultiplier *int - PricePerUnit *int - PixelsPerUnit *int - AutoAdjustPrice *bool - PricePerGateway *string - PricePerBroadcaster *string - BlockPollingInterval *int - Redeemer *bool - RedeemerAddr *string - Reward *bool - Monitor *bool - MetricsPerStream *bool - MetricsExposeClientIP *bool - MetadataQueueUri *string - MetadataAmqpExchange *string - MetadataPublishTimeout *time.Duration - Datadir *string - AIModelsDir *string - Objectstore *string - Recordstore *string - FVfailGsBucket *string - FVfailGsKey *string - AuthWebhookURL *string - OrchWebhookURL *string - OrchBlacklist *string - TestOrchAvail *bool - AIRunnerImage *string + Network *string + RtmpAddr *string + CliAddr *string + HttpAddr *string + ServiceAddr *string + OrchAddr *string + VerifierURL *string + EthController *string + VerifierPath *string + LocalVerify *bool + HttpIngest *bool + Orchestrator *bool + Transcoder *bool + AIWorker *bool + Gateway *bool + Broadcaster *bool + OrchSecret *string + TranscodingOptions *string + AIModels *string + MaxAttempts *int + SelectRandWeight *float64 + SelectStakeWeight *float64 + SelectPriceWeight *float64 + SelectPriceExpFactor *float64 + OrchPerfStatsURL *string + Region *string + MaxPricePerUnit *string + MinPerfScore *float64 + MaxSessions *string + CurrentManifest *bool + Nvidia *string + Netint *string + TestTranscoder *bool + EthAcctAddr *string + EthPassword *string + EthKeystorePath *string + EthOrchAddr *string + EthUrl *string + TxTimeout *time.Duration + MaxTxReplacements *int + GasLimit *int + MinGasPrice *int64 + MaxGasPrice *int + InitializeRound *bool + InitializeRoundMaxDelay *time.Duration + TicketEV *string + MaxFaceValue *string + MaxTicketEV *string + MaxTotalEV *string + DepositMultiplier *int + PricePerUnit *string + PixelsPerUnit *string + PriceFeedAddr *string + AutoAdjustPrice *bool + PricePerGateway *string + PricePerBroadcaster *string + BlockPollingInterval *int + Redeemer *bool + RedeemerAddr *string + Reward *bool + Monitor *bool + MetricsPerStream *bool + MetricsExposeClientIP *bool + MetadataQueueUri *string + MetadataAmqpExchange *string + MetadataPublishTimeout *time.Duration + Datadir *string + AIModelsDir *string + Objectstore *string + Recordstore *string + FVfailGsBucket *string + FVfailGsKey *string + AuthWebhookURL *string + OrchWebhookURL *string + OrchBlacklist *string + OrchMinLivepeerVersion *string + TestOrchAvail *bool + AIRunnerImage *string } // DefaultLivepeerConfig creates LivepeerConfig exactly the same as when no flags are passed to the livepeer process. @@ -204,13 +209,15 @@ func DefaultLivepeerConfig() LivepeerConfig { defaultMaxGasPrice := 0 defaultEthController := "" defaultInitializeRound := false + defaultInitializeRoundMaxDelay := 30 * time.Second defaultTicketEV := "8000000000" defaultMaxFaceValue := "0" defaultMaxTicketEV := "3000000000000" defaultMaxTotalEV := "20000000000000" defaultDepositMultiplier := 1 - defaultMaxPricePerUnit := 0 - defaultPixelsPerUnit := 1 + defaultMaxPricePerUnit := "0" + defaultPixelsPerUnit := "1" + defaultPriceFeedAddr := "0x639Fe6ab55C921f74e7fac1ee960C0B6293ba612" // ETH / USD price feed address on Arbitrum Mainnet defaultAutoAdjustPrice := true defaultPricePerGateway := "" defaultPricePerBroadcaster := "" @@ -242,6 +249,7 @@ func DefaultLivepeerConfig() LivepeerConfig { // API defaultAuthWebhookURL := "" defaultOrchWebhookURL := "" + defaultMinLivepeerVersion := "" // Flags defaultTestOrchAvail := true @@ -285,36 +293,38 @@ func DefaultLivepeerConfig() LivepeerConfig { AIRunnerImage: &defaultAIRunnerImage, // Onchain: - EthAcctAddr: &defaultEthAcctAddr, - EthPassword: &defaultEthPassword, - EthKeystorePath: &defaultEthKeystorePath, - EthOrchAddr: &defaultEthOrchAddr, - EthUrl: &defaultEthUrl, - TxTimeout: &defaultTxTimeout, - MaxTxReplacements: &defaultMaxTxReplacements, - GasLimit: &defaultGasLimit, - MaxGasPrice: &defaultMaxGasPrice, - EthController: &defaultEthController, - InitializeRound: &defaultInitializeRound, - TicketEV: &defaultTicketEV, - MaxFaceValue: &defaultMaxFaceValue, - MaxTicketEV: &defaultMaxTicketEV, - MaxTotalEV: &defaultMaxTotalEV, - DepositMultiplier: &defaultDepositMultiplier, - MaxPricePerUnit: &defaultMaxPricePerUnit, - PixelsPerUnit: &defaultPixelsPerUnit, - AutoAdjustPrice: &defaultAutoAdjustPrice, - PricePerGateway: &defaultPricePerGateway, - PricePerBroadcaster: &defaultPricePerBroadcaster, - BlockPollingInterval: &defaultBlockPollingInterval, - Redeemer: &defaultRedeemer, - RedeemerAddr: &defaultRedeemerAddr, - Monitor: &defaultMonitor, - MetricsPerStream: &defaultMetricsPerStream, - MetricsExposeClientIP: &defaultMetricsExposeClientIP, - MetadataQueueUri: &defaultMetadataQueueUri, - MetadataAmqpExchange: &defaultMetadataAmqpExchange, - MetadataPublishTimeout: &defaultMetadataPublishTimeout, + EthAcctAddr: &defaultEthAcctAddr, + EthPassword: &defaultEthPassword, + EthKeystorePath: &defaultEthKeystorePath, + EthOrchAddr: &defaultEthOrchAddr, + EthUrl: &defaultEthUrl, + TxTimeout: &defaultTxTimeout, + MaxTxReplacements: &defaultMaxTxReplacements, + GasLimit: &defaultGasLimit, + MaxGasPrice: &defaultMaxGasPrice, + EthController: &defaultEthController, + InitializeRound: &defaultInitializeRound, + InitializeRoundMaxDelay: &defaultInitializeRoundMaxDelay, + TicketEV: &defaultTicketEV, + MaxFaceValue: &defaultMaxFaceValue, + MaxTicketEV: &defaultMaxTicketEV, + MaxTotalEV: &defaultMaxTotalEV, + DepositMultiplier: &defaultDepositMultiplier, + MaxPricePerUnit: &defaultMaxPricePerUnit, + PixelsPerUnit: &defaultPixelsPerUnit, + PriceFeedAddr: &defaultPriceFeedAddr, + AutoAdjustPrice: &defaultAutoAdjustPrice, + PricePerGateway: &defaultPricePerGateway, + PricePerBroadcaster: &defaultPricePerBroadcaster, + BlockPollingInterval: &defaultBlockPollingInterval, + Redeemer: &defaultRedeemer, + RedeemerAddr: &defaultRedeemerAddr, + Monitor: &defaultMonitor, + MetricsPerStream: &defaultMetricsPerStream, + MetricsExposeClientIP: &defaultMetricsExposeClientIP, + MetadataQueueUri: &defaultMetadataQueueUri, + MetadataAmqpExchange: &defaultMetadataAmqpExchange, + MetadataPublishTimeout: &defaultMetadataPublishTimeout, // Ingest: HttpIngest: &defaultHttpIngest, @@ -335,6 +345,9 @@ func DefaultLivepeerConfig() LivepeerConfig { AuthWebhookURL: &defaultAuthWebhookURL, OrchWebhookURL: &defaultOrchWebhookURL, + // Versioning constraints + OrchMinLivepeerVersion: &defaultMinLivepeerVersion, + // Flags TestOrchAvail: &defaultTestOrchAvail, } @@ -504,152 +517,6 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { } } - var aiCaps []core.Capability - constraints := make(map[core.Capability]*core.Constraints) - - if *cfg.AIWorker { - gpus := []string{} - if *cfg.Nvidia != "" { - var err error - gpus, err = common.ParseAccelDevices(*cfg.Nvidia, ffmpeg.Nvidia) - if err != nil { - glog.Errorf("Error parsing -nvidia for devices: %v", err) - return - } - } - - modelsDir := *cfg.AIModelsDir - if modelsDir == "" { - var err error - modelsDir, err = filepath.Abs(path.Join(*cfg.Datadir, "models")) - if err != nil { - glog.Error("Error creating absolute path for models dir: %v", modelsDir) - return - } - } - - if err := os.MkdirAll(modelsDir, 0755); err != nil { - glog.Error("Error creating models dir %v", modelsDir) - return - } - - n.AIWorker, err = worker.NewWorker(*cfg.AIRunnerImage, gpus, modelsDir) - if err != nil { - glog.Errorf("Error starting AI worker: %v", err) - return - } - - if *cfg.AIModels != "" { - configs, err := core.ParseAIModelConfigs(*cfg.AIModels) - if err != nil { - glog.Errorf("Error parsing -aiModels: %v", err) - return - } - - for _, config := range configs { - modelConstraint := &core.ModelConstraint{Warm: config.Warm} - - // If the config contains a URL we call Warm() anyway because AIWorker will just register - // the endpoint for an external container - if config.Warm || config.URL != "" { - endpoint := worker.RunnerEndpoint{URL: config.URL, Token: config.Token} - if err := n.AIWorker.Warm(ctx, config.Pipeline, config.ModelID, endpoint, config.OptimizationFlags); err != nil { - glog.Errorf("Error AI worker warming %v container: %v", config.Pipeline, err) - return - } - } - - // Show warning if people set OptimizationFlags but not Warm. - if len(config.OptimizationFlags) > 0 && !config.Warm { - glog.Warningf("Model %v has 'optimization_flags' set without 'warm'. Optimization flags are currently only used for warm containers.", config.ModelID) - } - - switch config.Pipeline { - case "text-to-image": - _, ok := constraints[core.Capability_TextToImage] - if !ok { - aiCaps = append(aiCaps, core.Capability_TextToImage) - constraints[core.Capability_TextToImage] = &core.Constraints{ - Models: make(map[string]*core.ModelConstraint), - } - } - - constraints[core.Capability_TextToImage].Models[config.ModelID] = modelConstraint - - n.SetBasePriceForCap("default", core.Capability_TextToImage, config.ModelID, big.NewRat(config.PricePerUnit, config.PixelsPerUnit)) - case "image-to-image": - _, ok := constraints[core.Capability_ImageToImage] - if !ok { - aiCaps = append(aiCaps, core.Capability_ImageToImage) - constraints[core.Capability_ImageToImage] = &core.Constraints{ - Models: make(map[string]*core.ModelConstraint), - } - } - - constraints[core.Capability_ImageToImage].Models[config.ModelID] = modelConstraint - - n.SetBasePriceForCap("default", core.Capability_ImageToImage, config.ModelID, big.NewRat(config.PricePerUnit, config.PixelsPerUnit)) - case "image-to-video": - _, ok := constraints[core.Capability_ImageToVideo] - if !ok { - aiCaps = append(aiCaps, core.Capability_ImageToVideo) - constraints[core.Capability_ImageToVideo] = &core.Constraints{ - Models: make(map[string]*core.ModelConstraint), - } - } - - constraints[core.Capability_ImageToVideo].Models[config.ModelID] = modelConstraint - - n.SetBasePriceForCap("default", core.Capability_ImageToVideo, config.ModelID, big.NewRat(config.PricePerUnit, config.PixelsPerUnit)) - case "upscale": - _, ok := constraints[core.Capability_Upscale] - if !ok { - aiCaps = append(aiCaps, core.Capability_Upscale) - constraints[core.Capability_Upscale] = &core.Constraints{ - Models: make(map[string]*core.ModelConstraint), - } - } - - constraints[core.Capability_Upscale].Models[config.ModelID] = modelConstraint - - n.SetBasePriceForCap("default", core.Capability_Upscale, config.ModelID, big.NewRat(config.PricePerUnit, config.PixelsPerUnit)) - case "audio-to-text": - _, ok := constraints[core.Capability_AudioToText] - if !ok { - aiCaps = append(aiCaps, core.Capability_AudioToText) - constraints[core.Capability_AudioToText] = &core.Constraints{ - Models: make(map[string]*core.ModelConstraint), - } - } - - constraints[core.Capability_AudioToText].Models[config.ModelID] = modelConstraint - - n.SetBasePriceForCap("default", core.Capability_AudioToText, config.ModelID, big.NewRat(config.PricePerUnit, config.PixelsPerUnit)) - } - - if len(aiCaps) > 0 { - capability := aiCaps[len(aiCaps)-1] - price := n.GetBasePriceForCap("default", capability, config.ModelID) - glog.V(6).Infof("Capability %s (ID: %v) advertised with model constraint %s at price %d per %d unit", config.Pipeline, capability, config.ModelID, price.Num(), price.Denom()) - } - } - } else { - glog.Error("The '-aiModels' flag was set, but no model configuration was provided. Please specify the model configuration using the '-aiModels' flag.") - return - } - - defer func() { - ctx, cancel := context.WithTimeout(context.Background(), aiWorkerContainerStopTimeout) - defer cancel() - if err := n.AIWorker.Stop(ctx); err != nil { - glog.Errorf("Error stopping AI worker containers: %v", err) - return - } - - glog.Infof("Stopped AI worker containers") - }() - } - if *cfg.Redeemer { n.NodeType = core.RedeemerNode } else if *cfg.Orchestrator { @@ -707,7 +574,6 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { glog.Error(err) return } - } else { n.SelectionAlgorithm, err = createSelectionAlgorithm(cfg) if err != nil { @@ -887,6 +753,13 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { go serviceRegistryWatcher.Watch() defer serviceRegistryWatcher.Stop() + core.PriceFeedWatcher, err = watchers.NewPriceFeedWatcher(backend, *cfg.PriceFeedAddr) + // The price feed watch loop is started on demand on first subscribe. + if err != nil { + glog.Errorf("Failed to set up price feed watcher: %v", err) + return + } + n.Balances = core.NewAddressBalances(cleanupInterval) defer n.Balances.StopCleanup() @@ -908,16 +781,36 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { if *cfg.Orchestrator { // Set price per pixel base info + pixelsPerUnit, ok := new(big.Rat).SetString(*cfg.PixelsPerUnit) + if !ok || !pixelsPerUnit.IsInt() { + panic(fmt.Errorf("-pixelsPerUnit must be a valid integer, provided %v", *cfg.PixelsPerUnit)) + } + if pixelsPerUnit.Sign() <= 0 { + // Can't divide by 0 + panic(fmt.Errorf("-pixelsPerUnit must be > 0, provided %v", *cfg.PixelsPerUnit)) + } if cfg.PricePerUnit == nil && !*cfg.AIWorker { // Prevent orchestrators from unknowingly providing free transcoding panic(fmt.Errorf("-pricePerUnit must be set")) } else if cfg.PricePerUnit != nil { - if *cfg.PricePerUnit < 0 { - panic(fmt.Errorf("-pricePerUnit must be >= 0, provided %d", *cfg.PricePerUnit)) + pricePerUnit, currency, err := parsePricePerUnit(*cfg.PricePerUnit) + if err != nil { + panic(fmt.Errorf("-pricePerUnit must be a valid integer with an optional currency, provided %v", *cfg.PricePerUnit)) + } else if pricePerUnit.Sign() < 0 { + panic(fmt.Errorf("-pricePerUnit must be >= 0, provided %s", pricePerUnit)) } - - n.SetBasePrice("default", big.NewRat(int64(*cfg.PricePerUnit), int64(*cfg.PixelsPerUnit))) - glog.Infof("Price: %d wei for %d pixels\n ", *cfg.PricePerUnit, *cfg.PixelsPerUnit) + pricePerPixel := new(big.Rat).Quo(pricePerUnit, pixelsPerUnit) + autoPrice, err := core.NewAutoConvertedPrice(currency, pricePerPixel, func(price *big.Rat) { + unit := "pixel" + if *cfg.AIWorker { + unit = "compute unit" + } + glog.Infof("Price: %v wei per %s\n", price.FloatString(3), unit) + }) + if err != nil { + panic(fmt.Errorf("Error converting price: %v", err)) + } + n.SetBasePrice("default", autoPrice) } if *cfg.PricePerBroadcaster != "" { @@ -926,9 +819,15 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { } gatewayPrices := getGatewayPrices(*cfg.PricePerGateway) for _, p := range gatewayPrices { - price := big.NewRat(p.PricePerUnit, p.PixelsPerUnit) - n.SetBasePrice(p.EthAddress, price) - glog.Infof("Price: %v set for broadcaster %v", price.RatString(), p.EthAddress) + p := p + pricePerPixel := new(big.Rat).Quo(p.PricePerUnit, p.PixelsPerUnit) + autoPrice, err := core.NewAutoConvertedPrice(p.Currency, pricePerPixel, func(price *big.Rat) { + glog.Infof("Price: %v wei per pixel for gateway %v", price.FloatString(3), p.EthAddress) + }) + if err != nil { + panic(fmt.Errorf("Error converting price for gateway %s: %v", p.EthAddress, err)) + } + n.SetBasePrice(p.EthAddress, autoPrice) } n.AutoSessionLimit = *cfg.MaxSessions == "auto" @@ -1025,12 +924,30 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { n.Sender = pm.NewSender(n.Eth, timeWatcher, senderWatcher, maxEV, maxTotalEV, *cfg.DepositMultiplier) - if *cfg.PixelsPerUnit <= 0 { + pixelsPerUnit, ok := new(big.Rat).SetString(*cfg.PixelsPerUnit) + if !ok || !pixelsPerUnit.IsInt() { + panic(fmt.Errorf("-pixelsPerUnit must be a valid integer, provided %v", *cfg.PixelsPerUnit)) + } + if pixelsPerUnit.Sign() <= 0 { // Can't divide by 0 - panic(fmt.Errorf("The amount of pixels per unit must be greater than 0, provided %d instead\n", *cfg.PixelsPerUnit)) + panic(fmt.Errorf("-pixelsPerUnit must be > 0, provided %v", *cfg.PixelsPerUnit)) + } + maxPricePerUnit, currency, err := parsePricePerUnit(*cfg.MaxPricePerUnit) + if err != nil { + panic(fmt.Errorf("The maximum price per unit must be a valid integer with an optional currency, provided %v instead\n", *cfg.MaxPricePerUnit)) } - if *cfg.MaxPricePerUnit > 0 { - server.BroadcastCfg.SetMaxPrice(big.NewRat(int64(*cfg.MaxPricePerUnit), int64(*cfg.PixelsPerUnit))) + if maxPricePerUnit.Sign() > 0 { + pricePerPixel := new(big.Rat).Quo(maxPricePerUnit, pixelsPerUnit) + autoPrice, err := core.NewAutoConvertedPrice(currency, pricePerPixel, func(price *big.Rat) { + if monitor.Enabled { + monitor.MaxTranscodingPrice(price) + } + glog.Infof("Maximum transcoding price: %v wei per pixel\n ", price.FloatString(3)) + }) + if err != nil { + panic(fmt.Errorf("Error converting price: %v", err)) + } + server.BroadcastCfg.SetMaxPrice(autoPrice) } else { glog.Infof("Maximum transcoding price per pixel is not greater than 0: %v, broadcaster is currently set to accept ANY price.\n", *cfg.MaxPricePerUnit) glog.Infoln("To update the broadcaster's maximum acceptable transcoding price per pixel, use the CLI or restart the broadcaster with the appropriate 'maxPricePerUnit' and 'pixelsPerUnit' values") @@ -1103,7 +1020,7 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { if *cfg.InitializeRound { // Start round initializer // The node will only initialize rounds if it in the upcoming active set for the round - initializer := eth.NewRoundInitializer(n.Eth, timeWatcher) + initializer := eth.NewRoundInitializer(n.Eth, timeWatcher, *cfg.InitializeRoundMaxDelay) go func() { if err := initializer.Start(); err != nil { serviceErr <- err @@ -1143,6 +1060,201 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { }() } + var aiCaps []core.Capability + capabilityConstraints := make(map[core.Capability]*core.PerCapabilityConstraints) + + if *cfg.AIWorker { + gpus := []string{} + if *cfg.Nvidia != "" { + var err error + gpus, err = common.ParseAccelDevices(*cfg.Nvidia, ffmpeg.Nvidia) + if err != nil { + glog.Errorf("Error parsing -nvidia for devices: %v", err) + return + } + } + + modelsDir := *cfg.AIModelsDir + if modelsDir == "" { + var err error + modelsDir, err = filepath.Abs(path.Join(*cfg.Datadir, "models")) + if err != nil { + glog.Error("Error creating absolute path for models dir: %v", modelsDir) + return + } + } + + if err := os.MkdirAll(modelsDir, 0755); err != nil { + glog.Error("Error creating models dir %v", modelsDir) + return + } + + n.AIWorker, err = worker.NewWorker(*cfg.AIRunnerImage, gpus, modelsDir) + if err != nil { + glog.Errorf("Error starting AI worker: %v", err) + return + } + + // Get base pixels per unit. + pixelsPerUnitBase, ok := new(big.Rat).SetString(*cfg.PixelsPerUnit) + if !ok || !pixelsPerUnitBase.IsInt() { + panic(fmt.Errorf("-pixelsPerUnit must be a valid integer, provided %v", *cfg.PixelsPerUnit)) + } + if !ok || pixelsPerUnitBase.Sign() <= 0 { + // Can't divide by 0 + panic(fmt.Errorf("-pixelsPerUnit must be > 0, provided %v", *cfg.PixelsPerUnit)) + } + + if *cfg.AIModels != "" { + configs, err := core.ParseAIModelConfigs(*cfg.AIModels) + if err != nil { + glog.Errorf("Error parsing -aiModels: %v", err) + return + } + + for _, config := range configs { + modelConstraint := &core.ModelConstraint{Warm: config.Warm} + + var autoPrice *core.AutoConvertedPrice + if *cfg.Network != "offchain" { + pixelsPerUnit := config.PixelsPerUnit.Rat + if config.PixelsPerUnit.Rat == nil { + pixelsPerUnit = pixelsPerUnitBase + } else { + if !pixelsPerUnit.IsInt() || pixelsPerUnit.Sign() <= 0 { + panic(fmt.Errorf("'pixelsPerUnit' value specified for model '%v' in pipeline '%v' must be a valid positive integer, provided %v", config.ModelID, config.Pipeline, config.PixelsPerUnit)) + } + } + pricePerUnit := config.PricePerUnit.Rat + if err != nil { + panic(fmt.Errorf("'pricePerUnit' value specified for model '%v' in pipeline '%v' must be a valid integer with an optional currency, provided %v", config.ModelID, config.Pipeline, config.PricePerUnit)) + } else if pricePerUnit.Sign() < 0 { + panic(fmt.Errorf("'pricePerUnit' value specified for model '%v' in pipeline '%v' must be >= 0, provided %v", config.ModelID, config.Pipeline, config.PricePerUnit)) + } + pricePerPixel := new(big.Rat).Quo(pricePerUnit, pixelsPerUnit) + + autoPrice, err = core.NewAutoConvertedPrice(config.Currency, pricePerPixel, nil) + if err != nil { + panic(fmt.Errorf("error converting price: %v", err)) + } + } + + // If the config contains a URL we call Warm() anyway because AIWorker will just register + // the endpoint for an external container + if config.Warm || config.URL != "" { + endpoint := worker.RunnerEndpoint{URL: config.URL, Token: config.Token} + if err := n.AIWorker.Warm(ctx, config.Pipeline, config.ModelID, endpoint, config.OptimizationFlags); err != nil { + glog.Errorf("Error AI worker warming %v container: %v", config.Pipeline, err) + return + } + } + + // Show warning if people set OptimizationFlags but not Warm. + if len(config.OptimizationFlags) > 0 && !config.Warm { + glog.Warningf("Model %v has 'optimization_flags' set without 'warm'. Optimization flags are currently only used for warm containers.", config.ModelID) + } + + switch config.Pipeline { + case "text-to-image": + _, ok := capabilityConstraints[core.Capability_TextToImage] + if !ok { + aiCaps = append(aiCaps, core.Capability_TextToImage) + capabilityConstraints[core.Capability_TextToImage] = &core.PerCapabilityConstraints{ + Models: make(map[string]*core.ModelConstraint), + } + } + + capabilityConstraints[core.Capability_TextToImage].Models[config.ModelID] = modelConstraint + + if *cfg.Network != "offchain" { + n.SetBasePriceForCap("default", core.Capability_TextToImage, config.ModelID, autoPrice) + } + case "image-to-image": + _, ok := capabilityConstraints[core.Capability_ImageToImage] + if !ok { + aiCaps = append(aiCaps, core.Capability_ImageToImage) + capabilityConstraints[core.Capability_ImageToImage] = &core.PerCapabilityConstraints{ + Models: make(map[string]*core.ModelConstraint), + } + } + + capabilityConstraints[core.Capability_ImageToImage].Models[config.ModelID] = modelConstraint + + if *cfg.Network != "offchain" { + n.SetBasePriceForCap("default", core.Capability_ImageToImage, config.ModelID, autoPrice) + } + case "image-to-video": + _, ok := capabilityConstraints[core.Capability_ImageToVideo] + if !ok { + aiCaps = append(aiCaps, core.Capability_ImageToVideo) + capabilityConstraints[core.Capability_ImageToVideo] = &core.PerCapabilityConstraints{ + Models: make(map[string]*core.ModelConstraint), + } + } + + capabilityConstraints[core.Capability_ImageToVideo].Models[config.ModelID] = modelConstraint + + if *cfg.Network != "offchain" { + n.SetBasePriceForCap("default", core.Capability_ImageToVideo, config.ModelID, autoPrice) + } + case "upscale": + _, ok := capabilityConstraints[core.Capability_Upscale] + if !ok { + aiCaps = append(aiCaps, core.Capability_Upscale) + capabilityConstraints[core.Capability_Upscale] = &core.PerCapabilityConstraints{ + Models: make(map[string]*core.ModelConstraint), + } + } + + capabilityConstraints[core.Capability_Upscale].Models[config.ModelID] = modelConstraint + + if *cfg.Network != "offchain" { + n.SetBasePriceForCap("default", core.Capability_Upscale, config.ModelID, autoPrice) + } + case "audio-to-text": + _, ok := capabilityConstraints[core.Capability_AudioToText] + if !ok { + aiCaps = append(aiCaps, core.Capability_AudioToText) + capabilityConstraints[core.Capability_AudioToText] = &core.PerCapabilityConstraints{ + Models: make(map[string]*core.ModelConstraint), + } + } + + capabilityConstraints[core.Capability_AudioToText].Models[config.ModelID] = modelConstraint + + if *cfg.Network != "offchain" { + n.SetBasePriceForCap("default", core.Capability_AudioToText, config.ModelID, autoPrice) + } + } + + if len(aiCaps) > 0 { + capability := aiCaps[len(aiCaps)-1] + price := n.GetBasePriceForCap("default", capability, config.ModelID) + if *cfg.Network != "offchain" { + pricePerUnit := price.Num().Int64() / price.Denom().Int64() + glog.V(6).Infof("Capability %s (ID: %v) advertised with model constraint %s at price %d wei per compute unit", config.Pipeline, capability, config.ModelID, pricePerUnit) + } else { + glog.V(6).Infof("Capability %s (ID: %v) advertised with model constraint %s", config.Pipeline, capability, config.ModelID) + } + } + } + } else { + glog.Error("The '-aiModels' flag was set, but no model configuration was provided. Please specify the model configuration using the '-aiModels' flag.") + return + } + + defer func() { + ctx, cancel := context.WithTimeout(context.Background(), aiWorkerContainerStopTimeout) + defer cancel() + if err := n.AIWorker.Stop(ctx); err != nil { + glog.Errorf("Error stopping AI worker containers: %v", err) + return + } + + glog.Infof("Stopped AI worker containers") + }() + } + if *cfg.Objectstore != "" { prepared, err := drivers.PrepareOSURL(*cfg.Objectstore) if err != nil { @@ -1293,7 +1405,10 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { *cfg.CliAddr = defaultAddr(*cfg.CliAddr, "127.0.0.1", TranscoderCliPort) } - n.Capabilities = core.NewCapabilitiesWithConstraints(append(transcoderCaps, aiCaps...), core.MandatoryOCapabilities(), constraints) + n.Capabilities = core.NewCapabilitiesWithConstraints(append(transcoderCaps, aiCaps...), core.MandatoryOCapabilities(), core.Constraints{}, capabilityConstraints) + if cfg.OrchMinLivepeerVersion != nil { + n.Capabilities.SetMinVersionConstraint(*cfg.OrchMinLivepeerVersion) + } if drivers.NodeStorage == nil { // base URI will be empty for broadcasters; that's OK @@ -1596,9 +1711,10 @@ func checkOrStoreChainID(dbh *common.DB, chainID *big.Int) error { } type GatewayPrice struct { - EthAddress string `json:"ethaddress"` - PricePerUnit int64 `json:"priceperunit"` - PixelsPerUnit int64 `json:"pixelsperunit"` + EthAddress string + PricePerUnit *big.Rat + Currency string + PixelsPerUnit *big.Rat } func getGatewayPrices(gatewayPrices string) []GatewayPrice { @@ -1607,18 +1723,20 @@ func getGatewayPrices(gatewayPrices string) []GatewayPrice { } // Format of gatewayPrices json - // {"gateways":[{"ethaddress":"address1","priceperunit":1000,"pixelsperunit":1}, {"ethaddress":"address2","priceperunit":2000,"pixelsperunit":3}]} + // {"gateways":[{"ethaddress":"address1","priceperunit":0.5,"currency":"USD","pixelsperunit":1}, {"ethaddress":"address2","priceperunit":0.3,"currency":"USD","pixelsperunit":3}]} var pricesSet struct { Gateways []struct { - EthAddress string `json:"ethaddress"` - PixelsPerUnit json.RawMessage `json:"pixelsperunit"` - PricePerUnit json.RawMessage `json:"priceperunit"` + EthAddress string `json:"ethaddress"` + PixelsPerUnit core.JSONRat `json:"pixelsperunit"` + PricePerUnit core.JSONRat `json:"priceperunit"` + Currency string `json:"currency"` } `json:"gateways"` // TODO: Keep the old name for backwards compatibility, remove in the future Broadcasters []struct { - EthAddress string `json:"ethaddress"` - PixelsPerUnit json.RawMessage `json:"pixelsperunit"` - PricePerUnit json.RawMessage `json:"priceperunit"` + EthAddress string `json:"ethaddress"` + PixelsPerUnit core.JSONRat `json:"pixelsperunit"` + PricePerUnit core.JSONRat `json:"priceperunit"` + Currency string `json:"currency"` } `json:"broadcasters"` } pricesFileContent, _ := common.ReadFromFile(gatewayPrices) @@ -1639,20 +1757,11 @@ func getGatewayPrices(gatewayPrices string) []GatewayPrice { prices := make([]GatewayPrice, len(allGateways)) for i, p := range allGateways { - pixelsPerUnit, err := strconv.ParseInt(string(p.PixelsPerUnit), 10, 64) - if err != nil { - glog.Errorf("Pixels per unit could not be parsed for gateway %v. must be a valid number, provided %s", p.EthAddress, p.PixelsPerUnit) - continue - } - pricePerUnit, err := strconv.ParseInt(string(p.PricePerUnit), 10, 64) - if err != nil { - glog.Errorf("Price per unit could not be parsed for gateway %v. must be a valid number, provided %s", p.EthAddress, p.PricePerUnit) - continue - } prices[i] = GatewayPrice{ EthAddress: p.EthAddress, - PricePerUnit: pricePerUnit, - PixelsPerUnit: pixelsPerUnit, + Currency: p.Currency, + PricePerUnit: p.PricePerUnit.Rat, + PixelsPerUnit: p.PixelsPerUnit.Rat, } } @@ -1708,6 +1817,22 @@ func parseEthKeystorePath(ethKeystorePath string) (keystorePath, error) { return keystore, nil } +func parsePricePerUnit(pricePerUnitStr string) (*big.Rat, string, error) { + pricePerUnitRex := regexp.MustCompile(`^(\d+(\.\d+)?)([A-z][A-z0-9]*)?$`) + match := pricePerUnitRex.FindStringSubmatch(pricePerUnitStr) + if match == nil { + return nil, "", fmt.Errorf("price must be in the format of , provided %v", pricePerUnitStr) + } + price, currency := match[1], match[3] + + pricePerUnit, ok := new(big.Rat).SetString(price) + if !ok { + return nil, "", fmt.Errorf("price must be a valid number, provided %v", match[1]) + } + + return pricePerUnit, currency, nil +} + func refreshOrchPerfScoreLoop(ctx context.Context, region string, orchPerfScoreURL string, score *common.PerfScore) { for { refreshOrchPerfScore(region, orchPerfScoreURL, score) diff --git a/cmd/livepeer/starter/starter_test.go b/cmd/livepeer/starter/starter_test.go index e72e64c0b..60df92789 100644 --- a/cmd/livepeer/starter/starter_test.go +++ b/cmd/livepeer/starter/starter_test.go @@ -102,8 +102,8 @@ func TestParseGetGatewayPrices(t *testing.T) { assert.NotNil(prices) assert.Equal(2, len(prices)) - price1 := big.NewRat(prices[0].PricePerUnit, prices[0].PixelsPerUnit) - price2 := big.NewRat(prices[1].PricePerUnit, prices[1].PixelsPerUnit) + price1 := new(big.Rat).Quo(prices[0].PricePerUnit, prices[0].PixelsPerUnit) + price2 := new(big.Rat).Quo(prices[1].PricePerUnit, prices[1].PixelsPerUnit) assert.Equal(big.NewRat(1000, 1), price1) assert.Equal(big.NewRat(2000, 3), price2) } @@ -302,3 +302,91 @@ func TestUpdatePerfScore(t *testing.T) { } require.Equal(t, expScores, scores.Scores) } + +func TestParsePricePerUnit(t *testing.T) { + tests := []struct { + name string + pricePerUnitStr string + expectedPrice *big.Rat + expectedCurrency string + expectError bool + }{ + { + name: "Valid input with integer price", + pricePerUnitStr: "100USD", + expectedPrice: big.NewRat(100, 1), + expectedCurrency: "USD", + expectError: false, + }, + { + name: "Valid input with fractional price", + pricePerUnitStr: "0.13USD", + expectedPrice: big.NewRat(13, 100), + expectedCurrency: "USD", + expectError: false, + }, + { + name: "Valid input with decimal price", + pricePerUnitStr: "99.99EUR", + expectedPrice: big.NewRat(9999, 100), + expectedCurrency: "EUR", + expectError: false, + }, + { + name: "Lower case currency", + pricePerUnitStr: "99.99eur", + expectedPrice: big.NewRat(9999, 100), + expectedCurrency: "eur", + expectError: false, + }, + { + name: "Currency with numbers", + pricePerUnitStr: "420DOG3", + expectedPrice: big.NewRat(420, 1), + expectedCurrency: "DOG3", + expectError: false, + }, + { + name: "No specified currency, empty currency", + pricePerUnitStr: "100", + expectedPrice: big.NewRat(100, 1), + expectedCurrency: "", + expectError: false, + }, + { + name: "Explicit wei currency", + pricePerUnitStr: "100wei", + expectedPrice: big.NewRat(100, 1), + expectedCurrency: "wei", + expectError: false, + }, + { + name: "Invalid number", + pricePerUnitStr: "abcUSD", + expectedPrice: nil, + expectedCurrency: "", + expectError: true, + }, + { + name: "Negative price", + pricePerUnitStr: "-100USD", + expectedPrice: nil, + expectedCurrency: "", + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + price, currency, err := parsePricePerUnit(tt.pricePerUnitStr) + + if tt.expectError { + assert.Error(t, err) + } else { + require.NoError(t, err) + assert.True(t, tt.expectedPrice.Cmp(price) == 0) + assert.Equal(t, tt.expectedCurrency, currency) + } + }) + } +} diff --git a/cmd/livepeer_cli/wizard.go b/cmd/livepeer_cli/wizard.go index 01801e4fd..27ba13017 100644 --- a/cmd/livepeer_cli/wizard.go +++ b/cmd/livepeer_cli/wizard.go @@ -76,7 +76,7 @@ func (w *wizard) readStringAndValidate(validate func(in string) (string, error)) } } -// readStringYesOrNot reads a single line from stdin, trims spaces and +// readStringYesOrNo reads a single line from stdin, trims spaces and // checks that the string is either y or n func (w *wizard) readStringYesOrNo() string { return w.readStringAndValidate(func(in string) (string, error) { diff --git a/cmd/livepeer_cli/wizard_broadcast.go b/cmd/livepeer_cli/wizard_broadcast.go index b7967b0dd..8f2c59e91 100644 --- a/cmd/livepeer_cli/wizard_broadcast.go +++ b/cmd/livepeer_cli/wizard_broadcast.go @@ -57,10 +57,14 @@ func (w *wizard) setBroadcastConfig() { fmt.Printf("eg. 1 wei / 10 pixels = 0,1 wei per pixel \n") fmt.Printf("\n") fmt.Printf("Enter amount of pixels that make up a single unit (default: 1 pixel) - ") - pixelsPerUnit := w.readDefaultInt(1) + // Read numbers as strings not to lose precision and support big numbers + pixelsPerUnit := w.readDefaultString("1") fmt.Printf("\n") - fmt.Printf("Enter the maximum price to pay for %d pixels in Wei (required) - ", pixelsPerUnit) - maxPricePerUnit := w.readDefaultInt(0) + fmt.Printf("Enter the currency for the price per unit (default: Wei) - ") + currency := w.readDefaultString("Wei") + fmt.Printf("\n") + fmt.Printf("Enter the maximum price to pay for %s pixels in %s (default: 0) - ", pixelsPerUnit, currency) + maxPricePerUnit := w.readDefaultString("0") opts := w.allTranscodingOptions() if opts == nil { @@ -77,12 +81,18 @@ func (w *wizard) setBroadcastConfig() { } val := url.Values{ - "pixelsPerUnit": {fmt.Sprintf("%v", strconv.Itoa(pixelsPerUnit))}, - "maxPricePerUnit": {fmt.Sprintf("%v", strconv.Itoa(maxPricePerUnit))}, + "pixelsPerUnit": {fmt.Sprintf("%v", pixelsPerUnit)}, + "currency": {fmt.Sprintf("%v", currency)}, + "maxPricePerUnit": {fmt.Sprintf("%v", maxPricePerUnit)}, "transcodingOptions": {fmt.Sprintf("%v", transOpts)}, } - httpPostWithParams(fmt.Sprintf("http://%v:%v/setBroadcastConfig", w.host, w.httpPort), val) + result, ok := httpPostWithParams(fmt.Sprintf("http://%v:%v/setBroadcastConfig", w.host, w.httpPort), val) + if !ok { + fmt.Printf("Error applying configuration: %s\n", result) + } else { + fmt.Printf("Configuration applied successfully\n") + } } func (w *wizard) idListToVideoProfileList(idList string, opts map[int]string) (string, error) { diff --git a/cmd/livepeer_cli/wizard_stats.go b/cmd/livepeer_cli/wizard_stats.go index c7049e012..bea3b0790 100644 --- a/cmd/livepeer_cli/wizard_stats.go +++ b/cmd/livepeer_cli/wizard_stats.go @@ -171,14 +171,10 @@ func (w *wizard) broadcastStats() { } price, transcodingOptions := w.getBroadcastConfig() - priceString := "n/a" - if price != nil { - priceString = fmt.Sprintf("%v wei / %v pixels", price.Num().Int64(), price.Denom().Int64()) - } table := tablewriter.NewWriter(os.Stdout) data := [][]string{ - {"Max Price Per Pixel", priceString}, + {"Max Price Per Pixel", formatPricePerPixel(price)}, {"Broadcast Transcoding Options", transcodingOptions}, {"Deposit", eth.FormatUnits(sender.Deposit, "ETH")}, {"Reserve", eth.FormatUnits(sender.Reserve.FundsRemaining, "ETH")}, @@ -219,10 +215,6 @@ func (w *wizard) orchestratorStats() { fmt.Println("+------------------+") table := tablewriter.NewWriter(os.Stdout) - basePrice := "n/a" - if priceInfo != nil { - basePrice = fmt.Sprintf("%v wei / %v pixels", priceInfo.Num(), priceInfo.Denom()) - } data := [][]string{ {"Status", t.Status}, {"Active", strconv.FormatBool(t.Active)}, @@ -231,7 +223,7 @@ func (w *wizard) orchestratorStats() { {"Reward Cut (%)", eth.FormatPerc(t.RewardCut)}, {"Fee Cut (%)", eth.FormatPerc(flipPerc(t.FeeShare))}, {"Last Reward Round", t.LastRewardRound.String()}, - {"Base price per pixel", basePrice}, + {"Base price per pixel", formatPricePerPixel(priceInfo)}, {"Base price for broadcasters", b_prices}, } @@ -492,7 +484,9 @@ func (w *wizard) getBroadcasterPrices() (string, error) { return "", err } - var status map[string]interface{} + var status struct { + BroadcasterPrices map[string]*big.Rat `json:"BroadcasterPrices"` + } err = json.Unmarshal(result, &status) if err != nil { return "", err @@ -500,13 +494,21 @@ func (w *wizard) getBroadcasterPrices() (string, error) { prices := new(bytes.Buffer) - if broadcasterPrices, ok := status["BroadcasterPrices"]; ok { - for b, p := range broadcasterPrices.(map[string]interface{}) { - if b != "default" { - fmt.Fprintf(prices, "%s: %s per pixel\n", b, p) - } + for b, p := range status.BroadcasterPrices { + if b != "default" { + fmt.Fprintf(prices, "%s: %s\n", b, formatPricePerPixel(p)) } } return prices.String(), nil } + +func formatPricePerPixel(price *big.Rat) string { + if price == nil { + return "n/a" + } + if price.IsInt() { + return fmt.Sprintf("%v wei/pixel", price.RatString()) + } + return fmt.Sprintf("%v wei/pixel (%v/%v)", price.FloatString(3), price.Num(), price.Denom()) +} diff --git a/cmd/livepeer_cli/wizard_transcoder.go b/cmd/livepeer_cli/wizard_transcoder.go index b25644a74..2416aadbd 100644 --- a/cmd/livepeer_cli/wizard_transcoder.go +++ b/cmd/livepeer_cli/wizard_transcoder.go @@ -43,13 +43,7 @@ func myHostPort() string { return "https://" + ip + ":" + defaultRPCPort } -func (w *wizard) promptOrchestratorConfig() (float64, float64, int, int, string) { - var ( - blockRewardCut float64 - feeCut float64 - addr string - ) - +func (w *wizard) promptOrchestratorConfig() (blockRewardCut, feeCut float64, pricePerUnit, currency, pixelsPerUnit, serviceURI string) { orch, _, err := w.getOrchestratorInfo() if err != nil || orch == nil { fmt.Println("unable to get current reward cut and fee cut") @@ -68,17 +62,23 @@ func (w *wizard) promptOrchestratorConfig() (float64, float64, int, int, string) fmt.Println("eg. 1 wei / 10 pixels = 0,1 wei per pixel") fmt.Println() fmt.Printf("Enter amount of pixels that make up a single unit (default: 1 pixel) ") - pixelsPerUnit := w.readDefaultInt(1) - fmt.Printf("Enter the price for %d pixels in Wei (required) ", pixelsPerUnit) - pricePerUnit := w.readDefaultInt(0) + // Read numbers as strings not to lose precision and support big numbers + pixelsPerUnit = w.readDefaultString("1") + fmt.Println() + fmt.Printf("Enter the currency for the price per unit (default: Wei) ") + currency = w.readDefaultString("Wei") + fmt.Println() + fmt.Printf("Enter the price for %s pixels in %s (default: 0) ", pixelsPerUnit, currency) + pricePerUnit = w.readDefaultString("0") + var addr string if orch.ServiceURI == "" { addr = myHostPort() } else { addr = orch.ServiceURI } fmt.Printf("Enter the public host:port of node (default: %v)", addr) - serviceURI := w.readStringAndValidate(func(in string) (string, error) { + serviceURI = w.readStringAndValidate(func(in string) (string, error) { if "" == in { in = addr } @@ -92,7 +92,7 @@ func (w *wizard) promptOrchestratorConfig() (float64, float64, int, int, string) return in, nil }) - return blockRewardCut, 100 - feeCut, pricePerUnit, pixelsPerUnit, serviceURI + return blockRewardCut, 100 - feeCut, pricePerUnit, currency, pixelsPerUnit, serviceURI } func (w *wizard) activateOrchestrator() { @@ -196,13 +196,14 @@ func (w *wizard) setOrchestratorConfig() { } func (w *wizard) getOrchestratorConfigFormValues() url.Values { - blockRewardCut, feeShare, pricePerUnit, pixelsPerUnit, serviceURI := w.promptOrchestratorConfig() + blockRewardCut, feeShare, pricePerUnit, currency, pixelsPerUnit, serviceURI := w.promptOrchestratorConfig() return url.Values{ "blockRewardCut": {fmt.Sprintf("%v", blockRewardCut)}, "feeShare": {fmt.Sprintf("%v", feeShare)}, - "pricePerUnit": {fmt.Sprintf("%v", strconv.Itoa(pricePerUnit))}, - "pixelsPerUnit": {fmt.Sprintf("%v", strconv.Itoa(pixelsPerUnit))}, + "pricePerUnit": {fmt.Sprintf("%v", pricePerUnit)}, + "currency": {fmt.Sprintf("%v", currency)}, + "pixelsPerUnit": {fmt.Sprintf("%v", pixelsPerUnit)}, "serviceURI": {fmt.Sprintf("%v", serviceURI)}, } } @@ -319,18 +320,22 @@ func (w *wizard) setPriceForBroadcaster() { return in, nil }) - fmt.Println("Enter price per unit:") - price := w.readDefaultInt(0) - fmt.Println("Enter pixels per unit:") - pixels := w.readDefaultInt(1) + fmt.Println("Enter pixels per unit (default: 1 pixel)") + // Read numbers as strings not to lose precision and support big numbers + pixels := w.readDefaultString("1") + fmt.Println("Enter currency for the price per unit (default: Wei)") + currency := w.readDefaultString("Wei") + fmt.Println("Enter price per unit (default: 0)") + price := w.readDefaultString("0") data := url.Values{ - "pricePerUnit": {fmt.Sprintf("%v", strconv.Itoa(price))}, - "pixelsPerUnit": {fmt.Sprintf("%v", strconv.Itoa(pixels))}, + "pricePerUnit": {fmt.Sprintf("%v", price)}, + "currency": {fmt.Sprintf("%v", currency)}, + "pixelsPerUnit": {fmt.Sprintf("%v", pixels)}, "broadcasterEthAddr": {fmt.Sprintf("%v", ethaddr)}, } result, ok := httpPostWithParams(fmt.Sprintf("http://%v:%v/setPriceForBroadcaster", w.host, w.httpPort), data) if ok { - fmt.Printf("Price for broadcaster %v set to %v gwei per %v pixels", ethaddr, price, pixels) + fmt.Printf("Price for broadcaster %v set to %v %v per %v pixels", ethaddr, price, currency, pixels) return } else { fmt.Printf("Error setting price for broadcaster: %v", result) diff --git a/common/types.go b/common/types.go index 3e9800304..dcb258add 100644 --- a/common/types.go +++ b/common/types.go @@ -106,7 +106,7 @@ type OrchestratorPool interface { } type SelectionAlgorithm interface { - Select(addrs []ethcommon.Address, stakes map[ethcommon.Address]int64, prices map[ethcommon.Address]float64, perfScores map[ethcommon.Address]float64) ethcommon.Address + Select(ctx context.Context, addrs []ethcommon.Address, stakes map[ethcommon.Address]int64, maxPrice *big.Rat, prices map[ethcommon.Address]*big.Rat, perfScores map[ethcommon.Address]float64) ethcommon.Address } type PerfScore struct { diff --git a/core/ai.go b/core/ai.go index 772712e97..9ac86a307 100644 --- a/core/ai.go +++ b/core/ai.go @@ -4,6 +4,8 @@ import ( "context" "encoding/json" "errors" + "fmt" + "math/big" "os" "regexp" "strconv" @@ -23,34 +25,34 @@ type AI interface { HasCapacity(pipeline, modelID string) bool } +// Custom type to parse a big.Rat from a JSON number. +type JSONRat struct{ *big.Rat } + +func (s *JSONRat) UnmarshalJSON(data []byte) error { + rat, ok := new(big.Rat).SetString(string(data)) + if !ok { + return fmt.Errorf("value is not a number: %s", data) + } + *s = JSONRat{rat} + return nil +} + +func (s JSONRat) String() string { + return s.FloatString(2) +} + type AIModelConfig struct { Pipeline string `json:"pipeline"` ModelID string `json:"model_id"` URL string `json:"url,omitempty"` Token string `json:"token,omitempty"` Warm bool `json:"warm,omitempty"` - PricePerUnit int64 `json:"price_per_unit,omitempty"` - PixelsPerUnit int64 `json:"pixels_per_unit,omitempty"` + PricePerUnit JSONRat `json:"price_per_unit,omitempty"` + PixelsPerUnit JSONRat `json:"pixels_per_unit,omitempty"` + Currency string `json:"currency,omitempty"` OptimizationFlags worker.OptimizationFlags `json:"optimization_flags,omitempty"` } -func (config *AIModelConfig) UnmarshalJSON(data []byte) error { - // Custom type to avoid recursive calls to UnmarshalJSON - type AIModelConfigAlias AIModelConfig - // Set default values for fields - defaultConfig := &AIModelConfigAlias{ - PixelsPerUnit: 1, - } - - if err := json.Unmarshal(data, defaultConfig); err != nil { - return err - } - - *config = AIModelConfig(*defaultConfig) - - return nil -} - func ParseAIModelConfigs(config string) ([]AIModelConfig, error) { var configs []AIModelConfig diff --git a/core/autoconvertedprice.go b/core/autoconvertedprice.go new file mode 100644 index 000000000..b248937db --- /dev/null +++ b/core/autoconvertedprice.go @@ -0,0 +1,139 @@ +package core + +import ( + "context" + "fmt" + "math/big" + "strings" + "sync" + + "github.com/livepeer/go-livepeer/eth" + "github.com/livepeer/go-livepeer/eth/watchers" +) + +// PriceFeedWatcher is a global instance of a PriceFeedWatcher. It must be +// initialized before creating an AutoConvertedPrice instance. +var PriceFeedWatcher watchers.PriceFeedWatcher + +// Number of wei in 1 ETH +var weiPerETH = big.NewRat(1e18, 1) + +// AutoConvertedPrice represents a price that is automatically converted to wei +// based on the current price of ETH in a given currency. It uses the static +// PriceFeedWatcher that must be configured before creating an instance. +type AutoConvertedPrice struct { + cancelSubscription func() + onUpdate func(*big.Rat) + basePrice *big.Rat + + mu sync.RWMutex + current *big.Rat +} + +// NewFixedPrice creates a new AutoConvertedPrice with a fixed price in wei. +func NewFixedPrice(price *big.Rat) *AutoConvertedPrice { + return &AutoConvertedPrice{current: price} +} + +// NewAutoConvertedPrice creates a new AutoConvertedPrice instance with the given +// currency and base price. The onUpdate function is optional and gets called +// whenever the price is updated (also with the initial price). The Stop function +// must be called to free resources when the price is no longer needed. +func NewAutoConvertedPrice(currency string, basePrice *big.Rat, onUpdate func(*big.Rat)) (*AutoConvertedPrice, error) { + if onUpdate == nil { + onUpdate = func(*big.Rat) {} + } + + // Default currency (wei/eth) doesn't need the conversion loop + if lcurr := strings.ToLower(currency); lcurr == "" || lcurr == "wei" || lcurr == "eth" { + price := basePrice + if lcurr == "eth" { + price = new(big.Rat).Mul(basePrice, weiPerETH) + } + onUpdate(price) + return NewFixedPrice(price), nil + } + + if PriceFeedWatcher == nil { + return nil, fmt.Errorf("PriceFeedWatcher is not initialized") + } + + base, quote, err := PriceFeedWatcher.Currencies() + if err != nil { + return nil, fmt.Errorf("error getting price feed currencies: %v", err) + } + base, quote, currency = strings.ToUpper(base), strings.ToUpper(quote), strings.ToUpper(currency) + if base != "ETH" && quote != "ETH" { + return nil, fmt.Errorf("price feed does not have ETH as a currency (%v/%v)", base, quote) + } + if base != currency && quote != currency { + return nil, fmt.Errorf("price feed does not have %v as a currency (%v/%v)", currency, base, quote) + } + + currencyPrice, err := PriceFeedWatcher.Current() + if err != nil { + return nil, fmt.Errorf("error getting current price data: %v", err) + } + + ctx, cancel := context.WithCancel(context.Background()) + price := &AutoConvertedPrice{ + cancelSubscription: cancel, + onUpdate: onUpdate, + basePrice: basePrice, + current: new(big.Rat).Mul(basePrice, currencyToWeiMultiplier(currencyPrice, base)), + } + // Trigger the initial update with the current price + onUpdate(price.current) + + price.startAutoConvertLoop(ctx, base) + + return price, nil +} + +// Value returns the current price in wei. +func (a *AutoConvertedPrice) Value() *big.Rat { + a.mu.RLock() + defer a.mu.RUnlock() + return a.current +} + +// Stop unsubscribes from the price feed and frees resources from the +// auto-conversion loop. +func (a *AutoConvertedPrice) Stop() { + a.mu.Lock() + defer a.mu.Unlock() + if a.cancelSubscription != nil { + a.cancelSubscription() + a.cancelSubscription = nil + } +} + +func (a *AutoConvertedPrice) startAutoConvertLoop(ctx context.Context, baseCurrency string) { + priceUpdated := make(chan eth.PriceData, 1) + PriceFeedWatcher.Subscribe(ctx, priceUpdated) + go func() { + for { + select { + case <-ctx.Done(): + return + case currencyPrice := <-priceUpdated: + a.mu.Lock() + a.current = new(big.Rat).Mul(a.basePrice, currencyToWeiMultiplier(currencyPrice, baseCurrency)) + a.mu.Unlock() + + a.onUpdate(a.current) + } + } + }() +} + +// currencyToWeiMultiplier calculates the multiplier to convert the value +// specified in the custom currency to wei. +func currencyToWeiMultiplier(data eth.PriceData, baseCurrency string) *big.Rat { + ethMultipler := data.Price + if baseCurrency == "ETH" { + // Invert the multiplier if the quote is in the form ETH / X + ethMultipler = new(big.Rat).Inv(ethMultipler) + } + return new(big.Rat).Mul(ethMultipler, weiPerETH) +} diff --git a/core/autoconvertedprice_test.go b/core/autoconvertedprice_test.go new file mode 100644 index 000000000..54db6cfde --- /dev/null +++ b/core/autoconvertedprice_test.go @@ -0,0 +1,258 @@ +package core + +import ( + "context" + "math/big" + "testing" + "time" + + "github.com/livepeer/go-livepeer/eth" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestNewAutoConvertedPrice(t *testing.T) { + t.Run("PriceFeedWatcher not initialized", func(t *testing.T) { + _, err := NewAutoConvertedPrice("USD", big.NewRat(1, 1), nil) + require.Error(t, err) + }) + + watcherMock := NewPriceFeedWatcherMock(t) + PriceFeedWatcher = watcherMock + watcherMock.On("Currencies").Return("ETH", "USD", nil) + + t.Run("Fixed price for wei", func(t *testing.T) { + price, err := NewAutoConvertedPrice("wei", big.NewRat(1, 1), nil) + require.NoError(t, err) + require.Equal(t, big.NewRat(1, 1), price.Value()) + require.Nil(t, price.cancelSubscription) + }) + + t.Run("Auto-converted price for ETH", func(t *testing.T) { + price, err := NewAutoConvertedPrice("ETH", big.NewRat(2, 1), nil) + require.NoError(t, err) + require.Equal(t, big.NewRat(2e18, 1), price.Value()) // 2 ETH in wei + require.Nil(t, price.cancelSubscription) + }) + + t.Run("Auto-converted price for USD", func(t *testing.T) { + watcherMock.On("Current").Return(eth.PriceData{Price: big.NewRat(100, 1)}, nil) + watcherMock.On("Subscribe", mock.Anything, mock.Anything).Once() + price, err := NewAutoConvertedPrice("USD", big.NewRat(2, 1), nil) + require.NoError(t, err) + require.Equal(t, big.NewRat(2e16, 1), price.Value()) // 2 USD * 1/100 ETH/USD + require.NotNil(t, price.cancelSubscription) + price.Stop() + }) + + t.Run("Currency not supported by feed", func(t *testing.T) { + _, err := NewAutoConvertedPrice("GBP", big.NewRat(1, 1), nil) + require.Error(t, err) + }) + + t.Run("Currency ETH not supported by feed", func(t *testing.T) { + // set up a new mock to change the currencies returned + watcherMock := NewPriceFeedWatcherMock(t) + PriceFeedWatcher = watcherMock + watcherMock.On("Currencies").Return("wei", "USD", nil) + + _, err := NewAutoConvertedPrice("USD", big.NewRat(1, 1), nil) + require.Error(t, err) + }) + + t.Run("Auto-converted price for inverted quote", func(t *testing.T) { + // set up a new mock to change the currencies returned + watcherMock := NewPriceFeedWatcherMock(t) + PriceFeedWatcher = watcherMock + watcherMock.On("Currencies").Return("USD", "ETH", nil) + watcherMock.On("Current").Return(eth.PriceData{Price: big.NewRat(1, 420)}, nil) + watcherMock.On("Subscribe", mock.Anything, mock.Anything).Once() + price, err := NewAutoConvertedPrice("USD", big.NewRat(66, 1), nil) + require.NoError(t, err) + require.Equal(t, big.NewRat(11e17, 7), price.Value()) // 66 USD * 1/420 ETH/USD + require.NotNil(t, price.cancelSubscription) + price.Stop() + }) +} + +func TestAutoConvertedPrice_Update(t *testing.T) { + require := require.New(t) + watcherMock := NewPriceFeedWatcherMock(t) + PriceFeedWatcher = watcherMock + + watcherMock.On("Currencies").Return("ETH", "USD", nil) + watcherMock.On("Current").Return(eth.PriceData{Price: big.NewRat(3000, 1)}, nil) + + priceUpdatedChan := make(chan *big.Rat, 1) + onUpdate := func(price *big.Rat) { + priceUpdatedChan <- price + } + + var sink chan<- eth.PriceData + watcherMock.On("Subscribe", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { + sink = args.Get(1).(chan<- eth.PriceData) + }).Once() + + price, err := NewAutoConvertedPrice("USD", big.NewRat(50, 1), onUpdate) + require.NoError(err) + require.NotNil(t, price.cancelSubscription) + defer price.Stop() + watcherMock.AssertExpectations(t) + + require.Equal(big.NewRat(5e16, 3), price.Value()) // 50 USD * 1/3000 ETH/USD + require.Equal(big.NewRat(5e16, 3), <-priceUpdatedChan) // initial update must be sent + + // Simulate a price update + sink <- eth.PriceData{Price: big.NewRat(6000, 1)} + + select { + case updatedPrice := <-priceUpdatedChan: + require.Equal(big.NewRat(5e16, 6), updatedPrice) // 50 USD * 1/6000 USD/ETH + require.Equal(big.NewRat(5e16, 6), price.Value()) // must also udpate current value + case <-time.After(time.Second): + t.Fatal("Expected price update not received") + } +} + +func TestAutoConvertedPrice_Stop(t *testing.T) { + require := require.New(t) + watcherMock := NewPriceFeedWatcherMock(t) + PriceFeedWatcher = watcherMock + + watcherMock.On("Currencies").Return("ETH", "USD", nil) + watcherMock.On("Current").Return(eth.PriceData{Price: big.NewRat(100, 1)}, nil) + + var subsCtx context.Context + watcherMock.On("Subscribe", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { + subsCtx = args.Get(0).(context.Context) + }).Once() + + price, err := NewAutoConvertedPrice("USD", big.NewRat(50, 1), nil) + require.NoError(err) + require.NotNil(t, price.cancelSubscription) + + price.Stop() + require.Nil(price.cancelSubscription) + require.Error(subsCtx.Err()) +} + +func TestCurrencyToWeiMultiplier(t *testing.T) { + tests := []struct { + name string + data eth.PriceData + baseCurrency string + expectedWei *big.Rat + }{ + { + name: "Base currency is ETH", + data: eth.PriceData{Price: big.NewRat(500, 1)}, // 500 USD per ETH + baseCurrency: "ETH", + expectedWei: big.NewRat(1e18, 500), // (1 / 500 USD/ETH) * 1e18 wei/ETH + }, + { + name: "Base currency is not ETH", + data: eth.PriceData{Price: big.NewRat(1, 2000)}, // 1/2000 ETH per USD + baseCurrency: "USD", + expectedWei: big.NewRat(5e14, 1), // (1 * 1/2000 ETH/USD) * 1e18 wei/ETH + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := currencyToWeiMultiplier(tt.data, tt.baseCurrency) + assert.Equal(t, 0, tt.expectedWei.Cmp(result)) + }) + } +} + +// Auto-generated code from here down. +// +// Code generated by mockery v2.42.1. DO NOT EDIT. + +// PriceFeedWatcherMock is an autogenerated mock type for the PriceFeedWatcher type +type PriceFeedWatcherMock struct { + mock.Mock +} + +// Currencies provides a mock function with given fields: +func (_m *PriceFeedWatcherMock) Currencies() (string, string, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Currencies") + } + + var r0 string + var r1 string + var r2 error + if rf, ok := ret.Get(0).(func() (string, string, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func() string); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(string) + } + + if rf, ok := ret.Get(2).(func() error); ok { + r2 = rf() + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// Current provides a mock function with given fields: +func (_m *PriceFeedWatcherMock) Current() (eth.PriceData, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Current") + } + + var r0 eth.PriceData + var r1 error + if rf, ok := ret.Get(0).(func() (eth.PriceData, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() eth.PriceData); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(eth.PriceData) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Subscribe provides a mock function with given fields: ctx, sink +func (_m *PriceFeedWatcherMock) Subscribe(ctx context.Context, sink chan<- eth.PriceData) { + _m.Called(ctx, sink) +} + +// NewPriceFeedWatcherMock creates a new instance of PriceFeedWatcherMock. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewPriceFeedWatcherMock(t interface { + mock.TestingT + Cleanup(func()) +}) *PriceFeedWatcherMock { + mock := &PriceFeedWatcherMock{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/capabilities.go b/core/capabilities.go index 12eb7bbc6..237790e3c 100644 --- a/core/capabilities.go +++ b/core/capabilities.go @@ -3,9 +3,10 @@ package core import ( "errors" "fmt" - "sync" + "github.com/Masterminds/semver/v3" + "github.com/golang/glog" "github.com/livepeer/go-livepeer/net" "github.com/livepeer/go-tools/drivers" "github.com/livepeer/lpms/ffmpeg" @@ -20,16 +21,21 @@ type ModelConstraint struct { type Capability int type CapabilityString []uint64 type Constraints struct { + minVersion string +} +type PerCapabilityConstraints struct { // Models contains a *ModelConstraint for each supported model ID Models ModelConstraints } -type CapabilityConstraints map[Capability]*Constraints +type CapabilityConstraints map[Capability]*PerCapabilityConstraints type Capabilities struct { - bitstring CapabilityString - mandatories CapabilityString - constraints CapabilityConstraints - capacities map[Capability]int - mutex sync.Mutex + bitstring CapabilityString + mandatories CapabilityString + version string + constraints Constraints + capabilityConstraints CapabilityConstraints + capacities map[Capability]int + mutex sync.Mutex } type CapabilityTest struct { inVideoData []byte @@ -249,7 +255,7 @@ func (c1 CapabilityConstraints) CompatibleWith(c2 CapabilityConstraints) bool { return true } -func (c1 *Constraints) CompatibleWith(c2 *Constraints) bool { +func (c1 *PerCapabilityConstraints) CompatibleWith(c2 *PerCapabilityConstraints) bool { return c1.Models.CompatibleWith(c2.Models) } @@ -378,6 +384,51 @@ func JobCapabilities(params *StreamParameters, segPar *SegmentParameters) (*Capa return &Capabilities{bitstring: NewCapabilityString(capList)}, nil } +func (bcast *Capabilities) LivepeerVersionCompatibleWith(orch *net.Capabilities) bool { + if bcast == nil || orch == nil || bcast.constraints.minVersion == "" { + // should not happen, but just in case, return true by default + return true + } + if orch.Version == "" || orch.Version == "undefined" { + // Orchestrator/Transcoder version is not set, so it's incompatible + return false + } + + minVer, err := semver.NewVersion(bcast.constraints.minVersion) + if err != nil { + glog.Warningf("error while parsing minVersion: %v", err) + return true + } + ver, err := semver.NewVersion(orch.Version) + if err != nil { + glog.Warningf("error while parsing version: %v", err) + return false + } + + // // Ignore prerelease versions as in go-livepeer we actually define post-release suffixes + // minVerNoSuffix, _ := minVer.SetPrerelease("") + // verNoSuffix, _ := ver.SetPrerelease("") + + // return !verNoSuffix.LessThan(&minVerNoSuffix) + + // TODO: Remove AI-specific cases below when merging into master. + // NOTE: This logic was added to allow the version suffix (i.e. v0.7.6-ai.1) to be + // used correctly during the version constraint filtering. + minVerHasSuffix := minVer.Prerelease() != "" + verHasSuffix := ver.Prerelease() != "" + if !minVerHasSuffix || !verHasSuffix { + minVerNoSuffix, _ := minVer.SetPrerelease("") + verNoSuffix, _ := ver.SetPrerelease("") + minVer = &minVerNoSuffix + ver = &verNoSuffix + } + if minVer.Equal(ver) && minVerHasSuffix && !verHasSuffix { + return false + } + + return !ver.LessThan(minVer) +} + func (bcast *Capabilities) CompatibleWith(orch *net.Capabilities) bool { // Ensure bcast and orch are compatible with one another. @@ -387,6 +438,9 @@ func (bcast *Capabilities) CompatibleWith(orch *net.Capabilities) bool { // cf. common.CapabilityComparator return false } + if !bcast.LivepeerVersionCompatibleWith(orch) { + return false + } // For now, check this: // ( orch.mandatories AND bcast.bitstring ) == orch.mandatories && @@ -399,8 +453,8 @@ func (bcast *Capabilities) CompatibleWith(orch *net.Capabilities) bool { return false } - orchConstraints := CapabilitiesFromNetCapabilities(orch).constraints - if !bcast.constraints.CompatibleWith(orchConstraints) { + orchCapabilityConstraints := CapabilitiesFromNetCapabilities(orch).capabilityConstraints + if !bcast.capabilityConstraints.CompatibleWith(orchCapabilityConstraints) { return false } @@ -413,19 +467,19 @@ func (c *Capabilities) ToNetCapabilities() *net.Capabilities { } c.mutex.Lock() defer c.mutex.Unlock() - netCaps := &net.Capabilities{Bitstring: c.bitstring, Mandatories: c.mandatories, Capacities: make(map[uint32]uint32), Constraints: make(map[uint32]*net.Capabilities_Constraints)} + netCaps := &net.Capabilities{Bitstring: c.bitstring, Mandatories: c.mandatories, Version: c.version, Capacities: make(map[uint32]uint32), Constraints: &net.Capabilities_Constraints{MinVersion: c.constraints.minVersion}, CapabilityConstraints: make(map[uint32]*net.Capabilities_CapabilityConstraints)} for capability, capacity := range c.capacities { netCaps.Capacities[uint32(capability)] = uint32(capacity) } - for capability, constraints := range c.constraints { - models := make(map[string]*net.Capabilities_Constraints_ModelConstraint) + for capability, constraints := range c.capabilityConstraints { + models := make(map[string]*net.Capabilities_CapabilityConstraints_ModelConstraint) for modelID, modelConstraint := range constraints.Models { - models[modelID] = &net.Capabilities_Constraints_ModelConstraint{ + models[modelID] = &net.Capabilities_CapabilityConstraints_ModelConstraint{ Warm: modelConstraint.Warm, } } - netCaps.Constraints[uint32(capability)] = &net.Capabilities_Constraints{ + netCaps.CapabilityConstraints[uint32(capability)] = &net.Capabilities_CapabilityConstraints{ Models: models, } } @@ -437,10 +491,12 @@ func CapabilitiesFromNetCapabilities(caps *net.Capabilities) *Capabilities { return nil } coreCaps := &Capabilities{ - bitstring: caps.Bitstring, - mandatories: caps.Mandatories, - capacities: make(map[Capability]int), - constraints: make(map[Capability]*Constraints), + bitstring: caps.Bitstring, + mandatories: caps.Mandatories, + capacities: make(map[Capability]int), + version: caps.Version, + constraints: Constraints{minVersion: caps.Constraints.GetMinVersion()}, + capabilityConstraints: make(CapabilityConstraints), } if caps.Capacities == nil || len(caps.Capacities) == 0 { // build capacities map if not present (struct received from previous versions) @@ -458,13 +514,13 @@ func CapabilitiesFromNetCapabilities(caps *net.Capabilities) *Capabilities { } } - for capabilityInt, constraints := range caps.Constraints { + for capabilityInt, constraints := range caps.CapabilityConstraints { models := make(map[string]*ModelConstraint) for modelID, modelConstraint := range constraints.Models { models[modelID] = &ModelConstraint{Warm: modelConstraint.Warm} } - coreCaps.constraints[Capability(capabilityInt)] = &Constraints{ + coreCaps.capabilityConstraints[Capability(capabilityInt)] = &PerCapabilityConstraints{ Models: models, } } @@ -473,7 +529,7 @@ func CapabilitiesFromNetCapabilities(caps *net.Capabilities) *Capabilities { } func NewCapabilities(caps []Capability, m []Capability) *Capabilities { - c := &Capabilities{capacities: make(map[Capability]int)} + c := &Capabilities{capacities: make(map[Capability]int), version: LivepeerVersion, capabilityConstraints: make(CapabilityConstraints)} if len(caps) > 0 { c.bitstring = NewCapabilityString(caps) // initialize capacities to 1 by default, mandatory capabilities doesn't have capacities @@ -487,9 +543,10 @@ func NewCapabilities(caps []Capability, m []Capability) *Capabilities { return c } -func NewCapabilitiesWithConstraints(caps []Capability, m []Capability, constraints CapabilityConstraints) *Capabilities { +func NewCapabilitiesWithConstraints(caps []Capability, m []Capability, constraints Constraints, capabilityConstraints CapabilityConstraints) *Capabilities { c := NewCapabilities(caps, m) c.constraints = constraints + c.capabilityConstraints = capabilityConstraints return c } @@ -665,3 +722,16 @@ func (bcast *Capabilities) LegacyOnly() bool { } return bcast.bitstring.CompatibleWith(legacyCapabilityString) } + +func (bcast *Capabilities) SetMinVersionConstraint(minVersionConstraint string) { + if bcast != nil { + bcast.constraints.minVersion = minVersionConstraint + } +} + +func (bcast *Capabilities) MinVersionConstraint() string { + if bcast != nil { + return bcast.constraints.minVersion + } + return "" +} diff --git a/core/capabilities_test.go b/core/capabilities_test.go index 4f29c6c61..89e3d6210 100644 --- a/core/capabilities_test.go +++ b/core/capabilities_test.go @@ -331,6 +331,65 @@ func TestCapability_CompatibleWithNetCap(t *testing.T) { orch = NewCapabilities(nil, nil) bcast = NewCapabilities(nil, []Capability{1}) assert.True(bcast.CompatibleWith(orch.ToNetCapabilities())) + + // broadcaster is not compatible with orchestrator - old O's version + orch = NewCapabilities(nil, nil) + bcast = NewCapabilities(nil, nil) + bcast.constraints.minVersion = "0.4.1" + orch.version = "0.4.0" + assert.False(bcast.CompatibleWith(orch.ToNetCapabilities())) + + // broadcaster is compatible with orchestrator - the same version + orch = NewCapabilities(nil, nil) + bcast = NewCapabilities(nil, nil) + bcast.constraints.minVersion = "0.4.1" + orch.version = "0.4.1" + assert.True(bcast.CompatibleWith(orch.ToNetCapabilities())) + + // TODO: Remove AI-specific cases below when merging into master. + // NOTE: Additional logic was added to the `LivepeerVersionCompatibleWith` method in + // capabilities.go to achieve this behavior. + // AI broadcaster is compatible with AI orchestrator - higher ai suffix + orch = NewCapabilities(nil, nil) + bcast = NewCapabilities(nil, nil) + bcast.constraints.minVersion = "0.7.2" + orch.version = "0.7.2-ai.1" + assert.True(bcast.CompatibleWith(orch.ToNetCapabilities())) + + // AI broadcaster is not compatible with AI orchestrator - no ai suffix + orch = NewCapabilities(nil, nil) + bcast = NewCapabilities(nil, nil) + bcast.constraints.minVersion = "0.7.2-ai.1" + orch.version = "0.7.2" + assert.False(bcast.CompatibleWith(orch.ToNetCapabilities())) + + // AI broadcaster is not compatible with AI orchestrator - lower ai suffix + orch = NewCapabilities(nil, nil) + bcast = NewCapabilities(nil, nil) + bcast.constraints.minVersion = "0.7.2-ai.2" + orch.version = "0.7.2-ai.1" + assert.False(bcast.CompatibleWith(orch.ToNetCapabilities())) + + // AI broadcaster is not compatible with AI orchestrator - lower major version + orch = NewCapabilities(nil, nil) + bcast = NewCapabilities(nil, nil) + bcast.constraints.minVersion = "0.7.2-ai.2" + orch.version = "0.7.1-ai.1" + assert.False(bcast.CompatibleWith(orch.ToNetCapabilities())) + + // AI broadcaster is compatible with AI orchestrator - higher ai suffix + orch = NewCapabilities(nil, nil) + bcast = NewCapabilities(nil, nil) + bcast.constraints.minVersion = "0.7.2-ai.1" + orch.version = "0.7.2-ai.2" + assert.True(bcast.CompatibleWith(orch.ToNetCapabilities())) + + // AI broadcaster is compatible with AI orchestrator- higher major version + orch = NewCapabilities(nil, nil) + bcast = NewCapabilities(nil, nil) + bcast.constraints.minVersion = "0.7.2-ai.2" + orch.version = "0.7.3-ai.1" + assert.True(bcast.CompatibleWith(orch.ToNetCapabilities())) } func TestCapability_RoundTrip_Net(t *testing.T) { @@ -483,3 +542,131 @@ func TestCapabilities_LegacyCheck(t *testing.T) { assert.Len(legacyCapabilities, legacyLen) // sanity check no modifications } + +func TestLiveeerVersionCompatibleWith(t *testing.T) { + tests := []struct { + name string + broadcasterMinVersion string + transcoderVersion string + expected bool + }{ + { + name: "broadcaster required version is the same as the transcoder version", + broadcasterMinVersion: "0.4.1", + transcoderVersion: "0.4.1", + expected: true, + }, + { + name: "broadcaster required version is less than the transcoder version", + broadcasterMinVersion: "0.4.0", + transcoderVersion: "0.4.1", + expected: true, + }, + { + name: "broadcaster required version is more than the transcoder version", + broadcasterMinVersion: "0.4.2", + transcoderVersion: "0.4.1", + expected: false, + }, + { + name: "broadcaster required version is the same as the transcoder dirty version", + broadcasterMinVersion: "0.4.1", + transcoderVersion: "0.4.1-b3278dce-dirty", + expected: true, + }, + { + name: "broadcaster required version is before the transcoder dirty version", + broadcasterMinVersion: "0.4.0", + transcoderVersion: "0.4.1-b3278dce-dirty", + expected: true, + }, + { + name: "broadcaster required version is after the transcoder dirty version", + broadcasterMinVersion: "0.4.2", + transcoderVersion: "0.4.1-b3278dce-dirty", + expected: false, + }, + { + name: "broadcaster required version is empty", + broadcasterMinVersion: "", + transcoderVersion: "0.4.1", + expected: true, + }, + { + name: "both versions are undefined", + broadcasterMinVersion: "", + transcoderVersion: "", + expected: true, + }, + { + name: "transcoder version is empty", + broadcasterMinVersion: "0.4.0", + transcoderVersion: "", + expected: false, + }, + { + name: "transcoder version is undefined", + broadcasterMinVersion: "0.4.0", + transcoderVersion: "undefined", + expected: false, + }, + { + name: "unparsable broadcaster's min version", + broadcasterMinVersion: "nonparsablesemversion", + transcoderVersion: "0.4.1", + expected: true, + }, + { + name: "unparsable transcoder's version", + broadcasterMinVersion: "0.4.1", + transcoderVersion: "nonparsablesemversion", + expected: false, + }, + // TODO: Remove AI-specific cases below when merging into master. + // NOTE: Additional logic was added to the `LivepeerVersionCompatibleWith` method in + // capabilities.go to achieve this behavior. + { + name: "AI broadcaster required version has no AI suffix", + broadcasterMinVersion: "0.7.2", + transcoderVersion: "0.7.2-ai.1", + expected: true, + }, + { + name: "AI transcoder version has no AI suffix", + broadcasterMinVersion: "0.7.2-ai.1", + transcoderVersion: "0.7.2", + expected: false, + }, + { + name: "AI broadcaster required version AI suffix is higher than AI transcoder AI suffix", + broadcasterMinVersion: "0.7.2-ai.2", + transcoderVersion: "0.7.2-ai.1", + expected: false, + }, + { + name: "AI broadcaster required major version is higher than AI transcoder major version", + broadcasterMinVersion: "0.7.2-ai.2", + transcoderVersion: "0.7.2-ai.1", + expected: false, + }, + { + name: "AI broadcaster required version AI suffix is lower than AI transcoder AI suffix", + broadcasterMinVersion: "0.7.2-ai.1", + transcoderVersion: "0.7.2-ai.2", + expected: true, + }, + { + name: "AI broadcaster required major version is lower than AI transcoder major version", + broadcasterMinVersion: "0.7.2-ai.1", + transcoderVersion: "0.7.3-ai.1", + expected: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + bCapabilities := &Capabilities{constraints: Constraints{minVersion: tt.broadcasterMinVersion}} + tCapabilities := &Capabilities{version: tt.transcoderVersion} + assert.Equal(t, tt.expected, bCapabilities.LivepeerVersionCompatibleWith(tCapabilities.ToNetCapabilities())) + }) + } +} diff --git a/core/core_test.go b/core/core_test.go index 671c56c91..5346e2a3d 100644 --- a/core/core_test.go +++ b/core/core_test.go @@ -78,10 +78,10 @@ func TestTranscode(t *testing.T) { } // Check transcode result - if Over1Pct(len(tr.TranscodeData.Segments[0].Data), 218268) { // 144p + if Over1Pct(len(tr.TranscodeData.Segments[0].Data), 273352) { // 144p t.Error("Unexpected transcode result ", len(tr.TranscodeData.Segments[0].Data)) } - if Over1Pct(len(tr.TranscodeData.Segments[1].Data), 302868) { // 240p + if Over1Pct(len(tr.TranscodeData.Segments[1].Data), 378068) { // 240p t.Error("Unexpected transcode result ", len(tr.TranscodeData.Segments[1].Data)) } diff --git a/core/livepeernode.go b/core/livepeernode.go index e94c50af2..0d726ce65 100644 --- a/core/livepeernode.go +++ b/core/livepeernode.go @@ -64,20 +64,20 @@ func (t NodeType) String() string { } type CapabilityPriceMenu struct { - modelPrices map[string]*big.Rat + modelPrices map[string]*AutoConvertedPrice } func NewCapabilityPriceMenu() CapabilityPriceMenu { return CapabilityPriceMenu{ - modelPrices: make(map[string]*big.Rat), + modelPrices: make(map[string]*AutoConvertedPrice), } } -func (m CapabilityPriceMenu) SetPriceForModelID(modelID string, price *big.Rat) { +func (m CapabilityPriceMenu) SetPriceForModelID(modelID string, price *AutoConvertedPrice) { m.modelPrices[modelID] = price } -func (m CapabilityPriceMenu) PriceForModelID(modelID string) *big.Rat { +func (m CapabilityPriceMenu) PriceForModelID(modelID string) *AutoConvertedPrice { return m.modelPrices[modelID] } @@ -87,7 +87,7 @@ func NewCapabilityPrices() CapabilityPrices { return make(map[Capability]CapabilityPriceMenu) } -func (cp CapabilityPrices) SetPriceForModelID(cap Capability, modelID string, price *big.Rat) { +func (cp CapabilityPrices) SetPriceForModelID(cap Capability, modelID string, price *AutoConvertedPrice) { menu, ok := cp[cap] if !ok { menu = NewCapabilityPriceMenu() @@ -97,7 +97,7 @@ func (cp CapabilityPrices) SetPriceForModelID(cap Capability, modelID string, pr menu.SetPriceForModelID(modelID, price) } -func (cp CapabilityPrices) PriceForModelID(cap Capability, modelID string) *big.Rat { +func (cp CapabilityPrices) PriceForModelID(cap Capability, modelID string) *AutoConvertedPrice { menu, ok := cp[cap] if !ok { return nil @@ -139,7 +139,7 @@ type LivepeerNode struct { StorageConfigs map[string]*transcodeConfig storageMutex *sync.RWMutex // Transcoder private fields - priceInfo map[string]*big.Rat + priceInfo map[string]*AutoConvertedPrice priceInfoForCaps map[string]CapabilityPrices serviceURI url.URL segmentMutex *sync.RWMutex @@ -155,8 +155,8 @@ func NewLivepeerNode(e eth.LivepeerEthClient, wd string, dbh *common.DB) (*Livep AutoAdjustPrice: true, SegmentChans: make(map[ManifestID]SegmentChan), segmentMutex: &sync.RWMutex{}, - Capabilities: &Capabilities{capacities: map[Capability]int{}}, - priceInfo: make(map[string]*big.Rat), + Capabilities: &Capabilities{capacities: map[Capability]int{}, version: LivepeerVersion}, + priceInfo: make(map[string]*AutoConvertedPrice), priceInfoForCaps: make(map[string]CapabilityPrices), StorageConfigs: make(map[string]*transcodeConfig), storageMutex: &sync.RWMutex{}, @@ -176,12 +176,16 @@ func (n *LivepeerNode) SetServiceURI(newUrl *url.URL) { } // SetBasePrice sets the base price for an orchestrator on the node -func (n *LivepeerNode) SetBasePrice(b_eth_addr string, price *big.Rat) { +func (n *LivepeerNode) SetBasePrice(b_eth_addr string, price *AutoConvertedPrice) { addr := strings.ToLower(b_eth_addr) n.mu.Lock() defer n.mu.Unlock() + prevPrice := n.priceInfo[addr] n.priceInfo[addr] = price + if prevPrice != nil { + prevPrice.Stop() + } } // GetBasePrice gets the base price for an orchestrator @@ -190,17 +194,25 @@ func (n *LivepeerNode) GetBasePrice(b_eth_addr string) *big.Rat { n.mu.RLock() defer n.mu.RUnlock() - return n.priceInfo[addr] + price := n.priceInfo[addr] + if price == nil { + return nil + } + return price.Value() } func (n *LivepeerNode) GetBasePrices() map[string]*big.Rat { n.mu.RLock() defer n.mu.RUnlock() - return n.priceInfo + prices := make(map[string]*big.Rat) + for addr, price := range n.priceInfo { + prices[addr] = price.Value() + } + return prices } -func (n *LivepeerNode) SetBasePriceForCap(b_eth_addr string, cap Capability, modelID string, price *big.Rat) { +func (n *LivepeerNode) SetBasePriceForCap(b_eth_addr string, cap Capability, modelID string, price *AutoConvertedPrice) { addr := strings.ToLower(b_eth_addr) n.mu.Lock() defer n.mu.Unlock() @@ -224,7 +236,7 @@ func (n *LivepeerNode) GetBasePriceForCap(b_eth_addr string, cap Capability, mod return nil } - return prices.PriceForModelID(cap, modelID) + return prices.PriceForModelID(cap, modelID).Value() } // SetMaxFaceValue sets the faceValue upper limit for tickets received diff --git a/core/livepeernode_test.go b/core/livepeernode_test.go index 259992f89..d943086ba 100644 --- a/core/livepeernode_test.go +++ b/core/livepeernode_test.go @@ -162,8 +162,8 @@ func TestSetAndGetBasePrice(t *testing.T) { price := big.NewRat(1, 1) - n.SetBasePrice("default", price) - assert.Zero(n.priceInfo["default"].Cmp(price)) + n.SetBasePrice("default", NewFixedPrice(price)) + assert.Zero(n.priceInfo["default"].Value().Cmp(price)) assert.Zero(n.GetBasePrice("default").Cmp(price)) assert.Zero(n.GetBasePrices()["default"].Cmp(price)) @@ -172,10 +172,36 @@ func TestSetAndGetBasePrice(t *testing.T) { price1 := big.NewRat(2, 1) price2 := big.NewRat(3, 1) - n.SetBasePrice(addr1, price1) - n.SetBasePrice(addr2, price2) - assert.Zero(n.priceInfo[addr1].Cmp(price1)) - assert.Zero(n.priceInfo[addr2].Cmp(price2)) + n.SetBasePrice(addr1, NewFixedPrice(price1)) + n.SetBasePrice(addr2, NewFixedPrice(price2)) + assert.Zero(n.priceInfo[addr1].Value().Cmp(price1)) + assert.Zero(n.priceInfo[addr2].Value().Cmp(price2)) assert.Zero(n.GetBasePrices()[addr1].Cmp(price1)) assert.Zero(n.GetBasePrices()[addr2].Cmp(price2)) } + +func TestSetAndGetCapabilityPrices(t *testing.T) { + require := require.New(t) + assert := assert.New(t) + + n, err := NewLivepeerNode(nil, "", nil) + require.Nil(err) + + price := big.NewRat(1, 1) + + n.SetBasePriceForCap("default", Capability_TextToImage, "default", NewFixedPrice(price)) + assert.Zero(n.priceInfoForCaps["default"].PriceForModelID(Capability_TextToImage, "default").Value().Cmp(price)) + assert.Zero(n.GetBasePriceForCap("default", Capability_TextToImage, "default").Cmp(price)) + + addr1 := "0x0000000000000000000000000000000000000000" + addr2 := "0x1000000000000000000000000000000000000000" + price1 := big.NewRat(2, 1) + price2 := big.NewRat(3, 1) + + n.SetBasePriceForCap(addr1, Capability_TextToImage, "default", NewFixedPrice(price1)) + n.SetBasePriceForCap(addr2, Capability_ImageToImage, "default", NewFixedPrice(price2)) + assert.Zero(n.priceInfoForCaps[addr1].PriceForModelID(Capability_TextToImage, "default").Value().Cmp(price1)) + assert.Zero(n.priceInfoForCaps[addr2].PriceForModelID(Capability_ImageToImage, "default").Value().Cmp(price2)) + assert.Zero(n.GetBasePriceForCap(addr1, Capability_TextToImage, "default").Cmp(price1)) + assert.Zero(n.GetBasePriceForCap(addr2, Capability_ImageToImage, "default").Cmp(price2)) +} diff --git a/core/orch_test.go b/core/orch_test.go index 981661433..72aa9cb8b 100644 --- a/core/orch_test.go +++ b/core/orch_test.go @@ -245,7 +245,10 @@ func TestSelectTranscoder(t *testing.T) { strm := &StubTranscoderServer{manager: m, WithholdResults: false} strm2 := &StubTranscoderServer{manager: m} + LivepeerVersion = "0.4.1" capabilities := NewCapabilities(DefaultCapabilities(), []Capability{}) + LivepeerVersion = "undefined" + richCapabilities := NewCapabilities(append(DefaultCapabilities(), Capability_HEVC_Encode), []Capability{}) allCapabilities := NewCapabilities(append(DefaultCapabilities(), OptionalCapabilities()...), []Capability{}) @@ -259,7 +262,7 @@ func TestSelectTranscoder(t *testing.T) { go func() { m.Manage(strm, 1, capabilities.ToNetCapabilities()) }() time.Sleep(1 * time.Millisecond) // allow time for first stream to register go func() { m.Manage(strm2, 1, richCapabilities.ToNetCapabilities()); wg.Done() }() - time.Sleep(1 * time.Millisecond) // allow time for second stream to register + time.Sleep(1 * time.Millisecond) // allow time for second stream to register e for third stream to register assert.NotNil(m.liveTranscoders[strm]) assert.NotNil(m.liveTranscoders[strm2]) @@ -341,6 +344,20 @@ func TestSelectTranscoder(t *testing.T) { assert.Equal(1, t1.load) m.completeStreamSession(testSessionId) assert.Equal(0, t1.load) + + // assert one transcoder with the correct Livepeer version is selected + minVersionCapabilities := NewCapabilities(DefaultCapabilities(), []Capability{}) + minVersionCapabilities.SetMinVersionConstraint("0.4.0") + currentTranscoder, err = m.selectTranscoder(testSessionId, minVersionCapabilities) + assert.Nil(err) + m.completeStreamSession(testSessionId) + + // assert no transcoders available for min version higher than any transcoder + minVersionHighCapabilities := NewCapabilities(DefaultCapabilities(), []Capability{}) + minVersionHighCapabilities.SetMinVersionConstraint("0.4.2") + currentTranscoder, err = m.selectTranscoder(testSessionId, minVersionHighCapabilities) + assert.NotNil(err) + m.completeStreamSession(testSessionId) } func TestCompleteStreamSession(t *testing.T) { @@ -704,7 +721,7 @@ func TestProcessPayment_GivenRecipientError_ReturnsNil(t *testing.T) { } orch := NewOrchestrator(n, rm) orch.address = addr - orch.node.SetBasePrice("default", big.NewRat(0, 1)) + orch.node.SetBasePrice("default", NewFixedPrice(big.NewRat(0, 1))) recipient.On("TxCostMultiplier", mock.Anything).Return(big.NewRat(1, 1), nil) recipient.On("ReceiveTicket", mock.Anything, mock.Anything, mock.Anything).Return("", false, nil) @@ -785,7 +802,7 @@ func TestProcessPayment_ActiveOrchestrator(t *testing.T) { } orch := NewOrchestrator(n, rm) orch.address = addr - orch.node.SetBasePrice("default", big.NewRat(0, 1)) + orch.node.SetBasePrice("default", NewFixedPrice(big.NewRat(0, 1))) // orchestrator inactive -> error err := orch.ProcessPayment(context.Background(), defaultPayment(t), ManifestID("some manifest")) @@ -856,7 +873,7 @@ func TestProcessPayment_GivenLosingTicket_DoesNotRedeem(t *testing.T) { } orch := NewOrchestrator(n, rm) orch.address = addr - orch.node.SetBasePrice("default", big.NewRat(0, 1)) + orch.node.SetBasePrice("default", NewFixedPrice(big.NewRat(0, 1))) recipient.On("TxCostMultiplier", mock.Anything).Return(big.NewRat(1, 1), nil) recipient.On("ReceiveTicket", mock.Anything, mock.Anything, mock.Anything).Return("some sessionID", false, nil) @@ -888,7 +905,7 @@ func TestProcessPayment_GivenWinningTicket_RedeemError(t *testing.T) { } orch := NewOrchestrator(n, rm) orch.address = addr - orch.node.SetBasePrice("default", big.NewRat(0, 1)) + orch.node.SetBasePrice("default", NewFixedPrice(big.NewRat(0, 1))) manifestID := ManifestID("some manifest") sessionID := "some sessionID" @@ -928,7 +945,7 @@ func TestProcessPayment_GivenWinningTicket_Redeems(t *testing.T) { } orch := NewOrchestrator(n, rm) orch.address = addr - orch.node.SetBasePrice("default", big.NewRat(0, 1)) + orch.node.SetBasePrice("default", NewFixedPrice(big.NewRat(0, 1))) manifestID := ManifestID("some manifest") sessionID := "some sessionID" @@ -968,7 +985,7 @@ func TestProcessPayment_GivenMultipleWinningTickets_RedeemsAll(t *testing.T) { } orch := NewOrchestrator(n, rm) orch.address = addr - orch.node.SetBasePrice("default", big.NewRat(0, 1)) + orch.node.SetBasePrice("default", NewFixedPrice(big.NewRat(0, 1))) manifestID := ManifestID("some manifest") sessionID := "some sessionID" @@ -1038,7 +1055,7 @@ func TestProcessPayment_GivenConcurrentWinningTickets_RedeemsAll(t *testing.T) { } orch := NewOrchestrator(n, rm) orch.address = addr - orch.node.SetBasePrice("default", big.NewRat(0, 1)) + orch.node.SetBasePrice("default", NewFixedPrice(big.NewRat(0, 1))) manifestIDs := make([]string, 5) @@ -1097,7 +1114,7 @@ func TestProcessPayment_GivenReceiveTicketError_ReturnsError(t *testing.T) { } orch := NewOrchestrator(n, rm) orch.address = addr - orch.node.SetBasePrice("default", big.NewRat(0, 1)) + orch.node.SetBasePrice("default", NewFixedPrice(big.NewRat(0, 1))) manifestID := ManifestID("some manifest") @@ -1165,7 +1182,7 @@ func TestProcessPayment_PaymentError_DoesNotIncreaseCreditBalance(t *testing.T) } orch := NewOrchestrator(n, rm) orch.address = addr - orch.node.SetBasePrice("default", big.NewRat(0, 1)) + orch.node.SetBasePrice("default", NewFixedPrice(big.NewRat(0, 1))) manifestID := ManifestID("some manifest") paymentError := errors.New("ReceiveTicket error") @@ -1227,7 +1244,7 @@ func TestSufficientBalance_IsSufficient_ReturnsTrue(t *testing.T) { } orch := NewOrchestrator(n, rm) orch.address = addr - orch.node.SetBasePrice("default", big.NewRat(0, 1)) + orch.node.SetBasePrice("default", NewFixedPrice(big.NewRat(0, 1))) manifestID := ManifestID("some manifest") @@ -1265,7 +1282,7 @@ func TestSufficientBalance_IsNotSufficient_ReturnsFalse(t *testing.T) { } orch := NewOrchestrator(n, rm) orch.address = addr - orch.node.SetBasePrice("default", big.NewRat(0, 1)) + orch.node.SetBasePrice("default", NewFixedPrice(big.NewRat(0, 1))) manifestID := ManifestID("some manifest") @@ -1307,7 +1324,7 @@ func TestSufficientBalance_OffChainMode_ReturnsTrue(t *testing.T) { func TestTicketParams(t *testing.T) { n, _ := NewLivepeerNode(nil, "", nil) - n.priceInfo["default"] = big.NewRat(1, 1) + n.priceInfo["default"] = NewFixedPrice(big.NewRat(1, 1)) priceInfo := &net.PriceInfo{PricePerUnit: 1, PixelsPerUnit: 1} recipient := new(pm.MockRecipient) n.Recipient = recipient @@ -1388,7 +1405,7 @@ func TestPriceInfo(t *testing.T) { expPricePerPixel := big.NewRat(101, 100) n, _ := NewLivepeerNode(nil, "", nil) - n.SetBasePrice("default", basePrice) + n.SetBasePrice("default", NewFixedPrice(basePrice)) recipient := new(pm.MockRecipient) n.Recipient = recipient @@ -1406,7 +1423,7 @@ func TestPriceInfo(t *testing.T) { // basePrice = 10/1, txMultiplier = 100/1 => expPricePerPixel = 1010/100 basePrice = big.NewRat(10, 1) - n.SetBasePrice("default", basePrice) + n.SetBasePrice("default", NewFixedPrice(basePrice)) orch = NewOrchestrator(n, nil) expPricePerPixel = big.NewRat(1010, 100) @@ -1421,7 +1438,7 @@ func TestPriceInfo(t *testing.T) { // basePrice = 1/10, txMultiplier = 100 => expPricePerPixel = 101/1000 basePrice = big.NewRat(1, 10) - n.SetBasePrice("default", basePrice) + n.SetBasePrice("default", NewFixedPrice(basePrice)) orch = NewOrchestrator(n, nil) expPricePerPixel = big.NewRat(101, 1000) @@ -1435,7 +1452,7 @@ func TestPriceInfo(t *testing.T) { assert.Equal(priceInfo.PixelsPerUnit, expPrice.Denom().Int64()) // basePrice = 25/10 , txMultiplier = 100 => expPricePerPixel = 2525/1000 basePrice = big.NewRat(25, 10) - n.SetBasePrice("default", basePrice) + n.SetBasePrice("default", NewFixedPrice(basePrice)) orch = NewOrchestrator(n, nil) expPricePerPixel = big.NewRat(2525, 1000) @@ -1451,7 +1468,7 @@ func TestPriceInfo(t *testing.T) { // basePrice = 10/1 , txMultiplier = 100/10 => expPricePerPixel = 11 basePrice = big.NewRat(10, 1) txMultiplier = big.NewRat(100, 10) - n.SetBasePrice("default", basePrice) + n.SetBasePrice("default", NewFixedPrice(basePrice)) recipient = new(pm.MockRecipient) n.Recipient = recipient recipient.On("TxCostMultiplier", mock.Anything).Return(txMultiplier, nil) @@ -1470,7 +1487,7 @@ func TestPriceInfo(t *testing.T) { // basePrice = 10/1 , txMultiplier = 1/10 => expPricePerPixel = 110 basePrice = big.NewRat(10, 1) txMultiplier = big.NewRat(1, 10) - n.SetBasePrice("default", basePrice) + n.SetBasePrice("default", NewFixedPrice(basePrice)) recipient = new(pm.MockRecipient) n.Recipient = recipient recipient.On("TxCostMultiplier", mock.Anything).Return(txMultiplier, nil) @@ -1489,7 +1506,7 @@ func TestPriceInfo(t *testing.T) { // basePrice = 10, txMultiplier = 1 => expPricePerPixel = 20 basePrice = big.NewRat(10, 1) txMultiplier = big.NewRat(1, 1) - n.SetBasePrice("default", basePrice) + n.SetBasePrice("default", NewFixedPrice(basePrice)) recipient = new(pm.MockRecipient) n.Recipient = recipient recipient.On("TxCostMultiplier", mock.Anything).Return(txMultiplier, nil) @@ -1506,7 +1523,7 @@ func TestPriceInfo(t *testing.T) { assert.Equal(priceInfo.PixelsPerUnit, expPrice.Denom().Int64()) // basePrice = 0 => expPricePerPixel = 0 - n.SetBasePrice("default", big.NewRat(0, 1)) + n.SetBasePrice("default", NewFixedPrice(big.NewRat(0, 1))) orch = NewOrchestrator(n, nil) priceInfo, err = orch.PriceInfo(ethcommon.Address{}, "") @@ -1516,7 +1533,7 @@ func TestPriceInfo(t *testing.T) { // test no overflows basePrice = big.NewRat(25000, 1) - n.SetBasePrice("default", basePrice) + n.SetBasePrice("default", NewFixedPrice(basePrice)) faceValue, _ := new(big.Int).SetString("22245599237119512", 10) txCost := new(big.Int).Mul(big.NewInt(100000), big.NewInt(7500000000)) txMultiplier = new(big.Rat).SetFrac(faceValue, txCost) // 926899968213313/31250000000000 @@ -1572,7 +1589,7 @@ func TestPriceInfo_TxMultiplierError_ReturnsError(t *testing.T) { expError := errors.New("TxMultiplier Error") n, _ := NewLivepeerNode(nil, "", nil) - n.SetBasePrice("default", big.NewRat(1, 1)) + n.SetBasePrice("default", NewFixedPrice(big.NewRat(1, 1))) recipient := new(pm.MockRecipient) n.Recipient = recipient recipient.On("TxCostMultiplier", mock.Anything).Return(nil, expError) diff --git a/core/orchestrator.go b/core/orchestrator.go index 4ceea94bb..70975ff3a 100644 --- a/core/orchestrator.go +++ b/core/orchestrator.go @@ -350,7 +350,7 @@ func (orch *orchestrator) priceInfo(sender ethcommon.Address, manifestID Manifes for cap := range caps.Capacities { // If the capability does not have constraints (and thus any model constraints) skip it // because we only price a capability together with a model ID right now - constraints, ok := caps.Constraints[cap] + constraints, ok := caps.CapabilityConstraints[cap] if !ok { continue } @@ -1263,7 +1263,9 @@ func (rtm *RemoteTranscoderManager) selectTranscoder(sessionId string, caps *Cap findCompatibleTranscoder := func(rtm *RemoteTranscoderManager) int { for i := len(rtm.remoteTranscoders) - 1; i >= 0; i-- { // no capabilities = default capabilities, all transcoders must support them - if caps == nil || caps.bitstring.CompatibleWith(rtm.remoteTranscoders[i].capabilities.bitstring) { + if caps == nil || + (caps.bitstring.CompatibleWith(rtm.remoteTranscoders[i].capabilities.bitstring) && + caps.LivepeerVersionCompatibleWith(rtm.remoteTranscoders[i].capabilities.ToNetCapabilities())) { return i } } @@ -1315,7 +1317,7 @@ func (node *RemoteTranscoderManager) EndTranscodingSession(sessionId string) { panic("shouldn't be called on RemoteTranscoderManager") } -// completeStreamSessions end a stream session for a remote transcoder and decrements its load +// completeStreamSession end a stream session for a remote transcoder and decrements its load // caller should hold the mutex lock func (rtm *RemoteTranscoderManager) completeStreamSession(sessionId string) { t, ok := rtm.streamSessions[sessionId] diff --git a/core/testdata/rapid/TestCapability_RoundTrip_Net/TestCapability_RoundTrip_Net-20240729130524-3824236.fail b/core/testdata/rapid/TestCapability_RoundTrip_Net/TestCapability_RoundTrip_Net-20240729130524-3824236.fail new file mode 100644 index 000000000..bfa48715a --- /dev/null +++ b/core/testdata/rapid/TestCapability_RoundTrip_Net/TestCapability_RoundTrip_Net-20240729130524-3824236.fail @@ -0,0 +1,828 @@ +# 2024/07/29 13:05:24.695037 [TestCapability_RoundTrip_Net] [rapid] draw capLen: 54 +# 2024/07/29 13:05:24.695039 [TestCapability_RoundTrip_Net] [rapid] draw cap: 464 +# 2024/07/29 13:05:24.695040 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695040 [TestCapability_RoundTrip_Net] [rapid] draw cap: 461 +# 2024/07/29 13:05:24.695041 [TestCapability_RoundTrip_Net] [rapid] draw cap: 509 +# 2024/07/29 13:05:24.695041 [TestCapability_RoundTrip_Net] [rapid] draw cap: 429 +# 2024/07/29 13:05:24.695042 [TestCapability_RoundTrip_Net] [rapid] draw cap: 39 +# 2024/07/29 13:05:24.695042 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695043 [TestCapability_RoundTrip_Net] [rapid] draw cap: 9 +# 2024/07/29 13:05:24.695043 [TestCapability_RoundTrip_Net] [rapid] draw cap: 291 +# 2024/07/29 13:05:24.695044 [TestCapability_RoundTrip_Net] [rapid] draw cap: 4 +# 2024/07/29 13:05:24.695045 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695045 [TestCapability_RoundTrip_Net] [rapid] draw cap: 27 +# 2024/07/29 13:05:24.695045 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 +# 2024/07/29 13:05:24.695046 [TestCapability_RoundTrip_Net] [rapid] draw cap: 214 +# 2024/07/29 13:05:24.695046 [TestCapability_RoundTrip_Net] [rapid] draw cap: 192 +# 2024/07/29 13:05:24.695047 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695047 [TestCapability_RoundTrip_Net] [rapid] draw cap: 484 +# 2024/07/29 13:05:24.695047 [TestCapability_RoundTrip_Net] [rapid] draw cap: 165 +# 2024/07/29 13:05:24.695048 [TestCapability_RoundTrip_Net] [rapid] draw cap: 177 +# 2024/07/29 13:05:24.695049 [TestCapability_RoundTrip_Net] [rapid] draw cap: 213 +# 2024/07/29 13:05:24.695049 [TestCapability_RoundTrip_Net] [rapid] draw cap: 18 +# 2024/07/29 13:05:24.695050 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 +# 2024/07/29 13:05:24.695050 [TestCapability_RoundTrip_Net] [rapid] draw cap: 9 +# 2024/07/29 13:05:24.695050 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 +# 2024/07/29 13:05:24.695051 [TestCapability_RoundTrip_Net] [rapid] draw cap: 12 +# 2024/07/29 13:05:24.695051 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695051 [TestCapability_RoundTrip_Net] [rapid] draw cap: 437 +# 2024/07/29 13:05:24.695052 [TestCapability_RoundTrip_Net] [rapid] draw cap: 11 +# 2024/07/29 13:05:24.695052 [TestCapability_RoundTrip_Net] [rapid] draw cap: 39 +# 2024/07/29 13:05:24.695052 [TestCapability_RoundTrip_Net] [rapid] draw cap: 30 +# 2024/07/29 13:05:24.695053 [TestCapability_RoundTrip_Net] [rapid] draw cap: 12 +# 2024/07/29 13:05:24.695053 [TestCapability_RoundTrip_Net] [rapid] draw cap: 325 +# 2024/07/29 13:05:24.695053 [TestCapability_RoundTrip_Net] [rapid] draw cap: 204 +# 2024/07/29 13:05:24.695054 [TestCapability_RoundTrip_Net] [rapid] draw cap: 13 +# 2024/07/29 13:05:24.695054 [TestCapability_RoundTrip_Net] [rapid] draw cap: 403 +# 2024/07/29 13:05:24.695055 [TestCapability_RoundTrip_Net] [rapid] draw cap: 498 +# 2024/07/29 13:05:24.695055 [TestCapability_RoundTrip_Net] [rapid] draw cap: 512 +# 2024/07/29 13:05:24.695056 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 +# 2024/07/29 13:05:24.695057 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695058 [TestCapability_RoundTrip_Net] [rapid] draw cap: 136 +# 2024/07/29 13:05:24.695058 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 +# 2024/07/29 13:05:24.695058 [TestCapability_RoundTrip_Net] [rapid] draw cap: 169 +# 2024/07/29 13:05:24.695059 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695059 [TestCapability_RoundTrip_Net] [rapid] draw cap: 62 +# 2024/07/29 13:05:24.695059 [TestCapability_RoundTrip_Net] [rapid] draw cap: 181 +# 2024/07/29 13:05:24.695060 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695060 [TestCapability_RoundTrip_Net] [rapid] draw cap: 6 +# 2024/07/29 13:05:24.695061 [TestCapability_RoundTrip_Net] [rapid] draw cap: 12 +# 2024/07/29 13:05:24.695061 [TestCapability_RoundTrip_Net] [rapid] draw cap: 512 +# 2024/07/29 13:05:24.695062 [TestCapability_RoundTrip_Net] [rapid] draw cap: 177 +# 2024/07/29 13:05:24.695062 [TestCapability_RoundTrip_Net] [rapid] draw cap: 7 +# 2024/07/29 13:05:24.695063 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695064 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695064 [TestCapability_RoundTrip_Net] [rapid] draw cap: 400 +# 2024/07/29 13:05:24.695065 [TestCapability_RoundTrip_Net] [rapid] draw capLen: 42 +# 2024/07/29 13:05:24.695066 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695067 [TestCapability_RoundTrip_Net] [rapid] draw cap: 6 +# 2024/07/29 13:05:24.695068 [TestCapability_RoundTrip_Net] [rapid] draw cap: 368 +# 2024/07/29 13:05:24.695069 [TestCapability_RoundTrip_Net] [rapid] draw cap: 512 +# 2024/07/29 13:05:24.695069 [TestCapability_RoundTrip_Net] [rapid] draw cap: 10 +# 2024/07/29 13:05:24.695070 [TestCapability_RoundTrip_Net] [rapid] draw cap: 235 +# 2024/07/29 13:05:24.695071 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695071 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695071 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 +# 2024/07/29 13:05:24.695072 [TestCapability_RoundTrip_Net] [rapid] draw cap: 8 +# 2024/07/29 13:05:24.695072 [TestCapability_RoundTrip_Net] [rapid] draw cap: 263 +# 2024/07/29 13:05:24.695072 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695073 [TestCapability_RoundTrip_Net] [rapid] draw cap: 107 +# 2024/07/29 13:05:24.695073 [TestCapability_RoundTrip_Net] [rapid] draw cap: 301 +# 2024/07/29 13:05:24.695074 [TestCapability_RoundTrip_Net] [rapid] draw cap: 13 +# 2024/07/29 13:05:24.695074 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695074 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695075 [TestCapability_RoundTrip_Net] [rapid] draw cap: 11 +# 2024/07/29 13:05:24.695075 [TestCapability_RoundTrip_Net] [rapid] draw cap: 289 +# 2024/07/29 13:05:24.695076 [TestCapability_RoundTrip_Net] [rapid] draw cap: 19 +# 2024/07/29 13:05:24.695076 [TestCapability_RoundTrip_Net] [rapid] draw cap: 68 +# 2024/07/29 13:05:24.695077 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695079 [TestCapability_RoundTrip_Net] [rapid] draw cap: 102 +# 2024/07/29 13:05:24.695079 [TestCapability_RoundTrip_Net] [rapid] draw cap: 10 +# 2024/07/29 13:05:24.695079 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695080 [TestCapability_RoundTrip_Net] [rapid] draw cap: 6 +# 2024/07/29 13:05:24.695080 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695080 [TestCapability_RoundTrip_Net] [rapid] draw cap: 14 +# 2024/07/29 13:05:24.695081 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 +# 2024/07/29 13:05:24.695081 [TestCapability_RoundTrip_Net] [rapid] draw cap: 15 +# 2024/07/29 13:05:24.695081 [TestCapability_RoundTrip_Net] [rapid] draw cap: 54 +# 2024/07/29 13:05:24.695082 [TestCapability_RoundTrip_Net] [rapid] draw cap: 7 +# 2024/07/29 13:05:24.695082 [TestCapability_RoundTrip_Net] [rapid] draw cap: 94 +# 2024/07/29 13:05:24.695083 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695084 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695084 [TestCapability_RoundTrip_Net] [rapid] draw cap: 5 +# 2024/07/29 13:05:24.695084 [TestCapability_RoundTrip_Net] [rapid] draw cap: 206 +# 2024/07/29 13:05:24.695085 [TestCapability_RoundTrip_Net] [rapid] draw cap: 512 +# 2024/07/29 13:05:24.695085 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 +# 2024/07/29 13:05:24.695086 [TestCapability_RoundTrip_Net] [rapid] draw cap: 78 +# 2024/07/29 13:05:24.695086 [TestCapability_RoundTrip_Net] [rapid] draw cap: 11 +# 2024/07/29 13:05:24.695086 [TestCapability_RoundTrip_Net] [rapid] draw cap: 457 +# 2024/07/29 13:05:24.695203 [TestCapability_RoundTrip_Net] +# Error Trace: /home/ricks/development/livepeer/ai/go-livepeer/core/capabilities_test.go:371 +# /home/ricks/go/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /home/ricks/go/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /home/ricks/go/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /home/ricks/go/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:118 +# /home/ricks/development/livepeer/ai/go-livepeer/core/capabilities_test.go:356 +# Error: Not equal: +# expected: &core.Capabilities{bitstring:core.CapabilityString{0x4000008048043adf, 0x0, 0x22022000000100, 0x601001, 0x800000000, 0x20, 0x20200000090000, 0x2004001000012000, 0x1}, mandatories:core.CapabilityString{0x4000000008edef, 0x84040004010, 0x0, 0x80000004000, 0x200200000080, 0x1000000000000, 0x0, 0x200, 0x1}, version:"undefined", constraints:core.Constraints{minVersion:""}, capabilityConstraints:core.CapabilityConstraints(nil), capacities:map[core.Capability]int{0:1, 1:1, 2:1, 3:1, 4:1, 6:1, 7:1, 9:1, 11:1, 12:1, 13:1, 18:1, 27:1, 30:1, 39:1, 62:1, 136:1, 165:1, 169:1, 177:1, 181:1, 192:1, 204:1, 213:1, 214:1, 291:1, 325:1, 400:1, 403:1, 429:1, 437:1, 461:1, 464:1, 484:1, 498:1, 509:1, 512:1}, mutex:sync.Mutex{state:0, sema:0x0}} +# actual : &core.Capabilities{bitstring:core.CapabilityString{0x4000008048043adf, 0x0, 0x22022000000100, 0x601001, 0x800000000, 0x20, 0x20200000090000, 0x2004001000012000, 0x1}, mandatories:core.CapabilityString{0x4000000008edef, 0x84040004010, 0x0, 0x80000004000, 0x200200000080, 0x1000000000000, 0x0, 0x200, 0x1}, version:"undefined", constraints:core.Constraints{minVersion:""}, capabilityConstraints:core.CapabilityConstraints{}, capacities:map[core.Capability]int{0:1, 1:1, 2:1, 3:1, 4:1, 6:1, 7:1, 9:1, 11:1, 12:1, 13:1, 18:1, 27:1, 30:1, 39:1, 62:1, 136:1, 165:1, 169:1, 177:1, 181:1, 192:1, 204:1, 213:1, 214:1, 291:1, 325:1, 400:1, 403:1, 429:1, 437:1, 461:1, 464:1, 484:1, 498:1, 509:1, 512:1}, mutex:sync.Mutex{state:0, sema:0x0}} +# +# Diff: +# --- Expected +# +++ Actual +# @@ -27,3 +27,4 @@ +# }, +# - capabilityConstraints: (core.CapabilityConstraints) , +# + capabilityConstraints: (core.CapabilityConstraints) { +# + }, +# capacities: (map[core.Capability]int) (len=37) { +# Test: TestCapability_RoundTrip_Net +# 2024/07/29 13:05:24.695205 [TestCapability_RoundTrip_Net] [rapid] draw capLen: 0 +# 2024/07/29 13:05:24.695206 [TestCapability_RoundTrip_Net] [rapid] draw capLen: 88 +# 2024/07/29 13:05:24.695207 [TestCapability_RoundTrip_Net] [rapid] draw cap: 4 +# 2024/07/29 13:05:24.695207 [TestCapability_RoundTrip_Net] [rapid] draw cap: 8 +# 2024/07/29 13:05:24.695208 [TestCapability_RoundTrip_Net] [rapid] draw cap: 512 +# 2024/07/29 13:05:24.695208 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695208 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 +# 2024/07/29 13:05:24.695209 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 +# 2024/07/29 13:05:24.695210 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695210 [TestCapability_RoundTrip_Net] [rapid] draw cap: 79 +# 2024/07/29 13:05:24.695210 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695211 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695211 [TestCapability_RoundTrip_Net] [rapid] draw cap: 453 +# 2024/07/29 13:05:24.695212 [TestCapability_RoundTrip_Net] [rapid] draw cap: 232 +# 2024/07/29 13:05:24.695212 [TestCapability_RoundTrip_Net] [rapid] draw cap: 8 +# 2024/07/29 13:05:24.695212 [TestCapability_RoundTrip_Net] [rapid] draw cap: 10 +# 2024/07/29 13:05:24.695213 [TestCapability_RoundTrip_Net] [rapid] draw cap: 239 +# 2024/07/29 13:05:24.695214 [TestCapability_RoundTrip_Net] [rapid] draw cap: 20 +# 2024/07/29 13:05:24.695214 [TestCapability_RoundTrip_Net] [rapid] draw cap: 5 +# 2024/07/29 13:05:24.695215 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695216 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695217 [TestCapability_RoundTrip_Net] [rapid] draw cap: 265 +# 2024/07/29 13:05:24.695217 [TestCapability_RoundTrip_Net] [rapid] draw cap: 150 +# 2024/07/29 13:05:24.695218 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 +# 2024/07/29 13:05:24.695219 [TestCapability_RoundTrip_Net] [rapid] draw cap: 234 +# 2024/07/29 13:05:24.695219 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695223 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695223 [TestCapability_RoundTrip_Net] [rapid] draw cap: 62 +# 2024/07/29 13:05:24.695223 [TestCapability_RoundTrip_Net] [rapid] draw cap: 5 +# 2024/07/29 13:05:24.695224 [TestCapability_RoundTrip_Net] [rapid] draw cap: 512 +# 2024/07/29 13:05:24.695224 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695224 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 +# 2024/07/29 13:05:24.695225 [TestCapability_RoundTrip_Net] [rapid] draw cap: 17 +# 2024/07/29 13:05:24.695225 [TestCapability_RoundTrip_Net] [rapid] draw cap: 5 +# 2024/07/29 13:05:24.695226 [TestCapability_RoundTrip_Net] [rapid] draw cap: 75 +# 2024/07/29 13:05:24.695226 [TestCapability_RoundTrip_Net] [rapid] draw cap: 310 +# 2024/07/29 13:05:24.695226 [TestCapability_RoundTrip_Net] [rapid] draw cap: 172 +# 2024/07/29 13:05:24.695227 [TestCapability_RoundTrip_Net] [rapid] draw cap: 24 +# 2024/07/29 13:05:24.695227 [TestCapability_RoundTrip_Net] [rapid] draw cap: 88 +# 2024/07/29 13:05:24.695228 [TestCapability_RoundTrip_Net] [rapid] draw cap: 512 +# 2024/07/29 13:05:24.695228 [TestCapability_RoundTrip_Net] [rapid] draw cap: 446 +# 2024/07/29 13:05:24.695228 [TestCapability_RoundTrip_Net] [rapid] draw cap: 7 +# 2024/07/29 13:05:24.695229 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 +# 2024/07/29 13:05:24.695229 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 +# 2024/07/29 13:05:24.695229 [TestCapability_RoundTrip_Net] [rapid] draw cap: 114 +# 2024/07/29 13:05:24.695230 [TestCapability_RoundTrip_Net] [rapid] draw cap: 6 +# 2024/07/29 13:05:24.695230 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 +# 2024/07/29 13:05:24.695230 [TestCapability_RoundTrip_Net] [rapid] draw cap: 426 +# 2024/07/29 13:05:24.695231 [TestCapability_RoundTrip_Net] [rapid] draw cap: 13 +# 2024/07/29 13:05:24.695232 [TestCapability_RoundTrip_Net] [rapid] draw cap: 13 +# 2024/07/29 13:05:24.695232 [TestCapability_RoundTrip_Net] [rapid] draw cap: 6 +# 2024/07/29 13:05:24.695233 [TestCapability_RoundTrip_Net] [rapid] draw cap: 29 +# 2024/07/29 13:05:24.695233 [TestCapability_RoundTrip_Net] [rapid] draw cap: 130 +# 2024/07/29 13:05:24.695233 [TestCapability_RoundTrip_Net] [rapid] draw cap: 15 +# 2024/07/29 13:05:24.695234 [TestCapability_RoundTrip_Net] [rapid] draw cap: 196 +# 2024/07/29 13:05:24.695234 [TestCapability_RoundTrip_Net] [rapid] draw cap: 478 +# 2024/07/29 13:05:24.695234 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695235 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695235 [TestCapability_RoundTrip_Net] [rapid] draw cap: 10 +# 2024/07/29 13:05:24.695236 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 +# 2024/07/29 13:05:24.695236 [TestCapability_RoundTrip_Net] [rapid] draw cap: 406 +# 2024/07/29 13:05:24.695236 [TestCapability_RoundTrip_Net] [rapid] draw cap: 145 +# 2024/07/29 13:05:24.695237 [TestCapability_RoundTrip_Net] [rapid] draw cap: 241 +# 2024/07/29 13:05:24.695237 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 +# 2024/07/29 13:05:24.695237 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 +# 2024/07/29 13:05:24.695238 [TestCapability_RoundTrip_Net] [rapid] draw cap: 5 +# 2024/07/29 13:05:24.695238 [TestCapability_RoundTrip_Net] [rapid] draw cap: 229 +# 2024/07/29 13:05:24.695239 [TestCapability_RoundTrip_Net] [rapid] draw cap: 47 +# 2024/07/29 13:05:24.695239 [TestCapability_RoundTrip_Net] [rapid] draw cap: 512 +# 2024/07/29 13:05:24.695240 [TestCapability_RoundTrip_Net] [rapid] draw cap: 5 +# 2024/07/29 13:05:24.695240 [TestCapability_RoundTrip_Net] [rapid] draw cap: 270 +# 2024/07/29 13:05:24.695241 [TestCapability_RoundTrip_Net] [rapid] draw cap: 148 +# 2024/07/29 13:05:24.695241 [TestCapability_RoundTrip_Net] [rapid] draw cap: 292 +# 2024/07/29 13:05:24.695241 [TestCapability_RoundTrip_Net] [rapid] draw cap: 146 +# 2024/07/29 13:05:24.695242 [TestCapability_RoundTrip_Net] [rapid] draw cap: 411 +# 2024/07/29 13:05:24.695242 [TestCapability_RoundTrip_Net] [rapid] draw cap: 49 +# 2024/07/29 13:05:24.695242 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 +# 2024/07/29 13:05:24.695243 [TestCapability_RoundTrip_Net] [rapid] draw cap: 132 +# 2024/07/29 13:05:24.695243 [TestCapability_RoundTrip_Net] [rapid] draw cap: 9 +# 2024/07/29 13:05:24.695243 [TestCapability_RoundTrip_Net] [rapid] draw cap: 193 +# 2024/07/29 13:05:24.695244 [TestCapability_RoundTrip_Net] [rapid] draw cap: 510 +# 2024/07/29 13:05:24.695244 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 +# 2024/07/29 13:05:24.695245 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695245 [TestCapability_RoundTrip_Net] [rapid] draw cap: 22 +# 2024/07/29 13:05:24.695245 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 +# 2024/07/29 13:05:24.695246 [TestCapability_RoundTrip_Net] [rapid] draw cap: 136 +# 2024/07/29 13:05:24.695246 [TestCapability_RoundTrip_Net] [rapid] draw cap: 408 +# 2024/07/29 13:05:24.695246 [TestCapability_RoundTrip_Net] [rapid] draw cap: 11 +# 2024/07/29 13:05:24.695247 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# 2024/07/29 13:05:24.695247 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 +# +v0.4.8#8355047278167994326 +0x1ffe553afdc0f7 +0xf45b3a3026fb1 +0x36 +0x19e8d3757890a9 +0x1ece3789377bd6 +0x254 +0x205 +0x1d0 +0xc696ee1730946 +0xa5bfc6000ae4 +0x1 +0x1e767e042e2f9d +0x176d810cfc0f2b +0x267 +0x1cd +0x2a57c0b86b7d2 +0x1e916bccdef442 +0x1fd +0xcf6dc68c6ae6f +0x17398c89c02dec +0x1ad +0x131c5a873b987b +0x157a8063db5cc8 +0x27 +0x1987e962a4ed1 +0x12edc22943d81 +0x0 +0x163376461fd54d +0x9ce4ce56c30ba +0x9 +0x1e5486cb3b6174 +0x16c0f7f21eb8cb +0x123 +0xf0a4d4dd1e518 +0x1ac2347bba71a3 +0x4 +0x169e45fe17b9c4 +0x5c8baba4436ce +0x1 +0x1fa586fec7e96b +0x120720f4f21290 +0x1b +0x4fb6259a712e2 +0x37f35a9300112 +0x3 +0x1935b725e9e96 +0x187a601d1528ca +0xd6 +0x7913ebe9dc13a +0x142e0f585b2dac +0xc0 +0x196f40656a41ec +0x3af286f273a5d +0x0 +0x1a2c3ac100cc8e +0x1a36899482617e +0x3a3 +0x1e4 +0x1b6b16ee1d7b0 +0x1cbffa9417a94e +0xa5 +0x6edec3d67143c +0x127c8c567c73c4 +0xb1 +0x188f8cfc3194f8 +0x15a4da498b416b +0xd5 +0x1ac94151d50f1b +0x1cbdfcfdc9aba8 +0x12 +0x4424d7c0cfd27 +0xc2c679bb08e83 +0x3 +0x13a41a68b5e7b5 +0xe824a9c93fb79 +0x9 +0x104eae942c2d3a +0x8401f4f6add60 +0x2 +0x1a85ae65d696b5 +0xd9a97b75d45a0 +0xc +0x1dcdacf4d1f538 +0x5b79db0b9ce2e +0x1 +0x105690b2cc5155 +0x160e817f276ba9 +0x1b5 +0x1aef64c8f37d26 +0xaf0d1bd20e7f0 +0xb +0x124d67659fe95 +0xef30a4d402397 +0x27 +0xea3815f25e942 +0xd3dd513625b5f +0x1e +0xa34a45d0353c0 +0x181b4b8c96b635 +0x2cf +0x390 +0xc +0x1249d6d7171e3e +0x18466e9bb7d281 +0x3c9 +0x358 +0x145 +0x99299469331c0 +0x1225c985e1f9d7 +0xcc +0x8ae4b0b2d445c +0xa0005e7ee8688 +0xd +0x156193b8049849 +0x1beecbb840472f +0x3c1 +0x318 +0x2ab +0x193 +0x14816d4d091040 +0x1e18e4ad3afb90 +0x312 +0x1f2 +0x16d98afc38a5eb +0x1ffb0857cf3f4e +0xffffffffffffffff +0x63bc8e9d1b67c +0x670439aa47cba +0x3 +0x7d862db5053c3 +0x908d39c551ddc +0x0 +0x1e5beb91ed0b7 +0x11cff49a778ef7 +0x88 +0xc149b27cdeaff +0x5ae64d015a7f1 +0x2 +0x3f1601a4584c9 +0x12b7466721c2e3 +0xa9 +0x141e20558b5b71 +0x1b482d7eb0eab +0x1 +0xfe9be41b1a7d1 +0x11fbe041d5e3a2 +0x3e +0xc05c575caa3e2 +0x12fd3f55f92749 +0xb5 +0xfef307a80649c +0x694d773b6e563 +0x0 +0x1284e54af89718 +0x8f1a506967360 +0x6 +0x1eecbbac2653a2 +0xafb39c58bdb30 +0xc +0x1397764b96e3d7 +0x1fcf6c00def779 +0xffffffffffffffff +0xd943ace824438 +0x1234ba12e2b5d9 +0xb1 +0xc079ec12eaad5 +0x7e0bf0514df4a +0x7 +0x151b5694ff1a29 +0x3699052aae4ed +0x0 +0x160ee2a3299343 +0x94725373b3b5e +0x0 +0x19762e14c2a5a5 +0x1d3d3495650207 +0x190 +0x16cfffcbf32568 +0xe62e37e3841db +0x2a +0x11079c4e5b4840 +0x54fc022a5fc28 +0x0 +0x12cb7927ef68c +0xab6a464b9bb44 +0x6 +0x1396451ba612f3 +0x15955083a4f426 +0x2c4 +0x3c2 +0x170 +0x8ad02f4160198 +0x1f2e292b02fc83 +0xffffffffffffffff +0x947f2842e50d6 +0xeaa1a5622e3a4 +0xa +0x1a1704c8278973 +0x12a05ed66f4c0a +0xeb +0x1114fdc6207cd2 +0x3bb4cfcac042b +0x1 +0x1b3b74b3d01456 +0x531f320b2531b +0x1 +0x197e4b75069d1c +0x4d8176640b92e +0x3 +0x1b012144f0fa51 +0x10fae59448f46a +0x8 +0x10539663fa4035 +0x164c0b334cded1 +0x37c +0x107 +0x125a9a06e7bba5 +0xb9b43c76717b +0x1 +0x1cf2aeb6603cb3 +0x149038ed5d9d11 +0x6b +0xf173ebe9135e5 +0x1811a49e35de09 +0x12d +0xb813b577702a +0xafe2049d3f562 +0xd +0x1ba2b7b064c255 +0x20607cec5b805 +0x1 +0x16c2154199ef32 +0x23f3c71d80c9b +0x1 +0x1106d63649cf19 +0xa2b99bca8e7ac +0xb +0xa6d34ffecd0a5 +0x159f02878e5f6d +0x2ea +0x2ba +0x31b +0x121 +0x17762d304d229e +0x1ae07bc79e4410 +0x13 +0x16520166b2318f +0x18b9eed5884993 +0x39e +0x44 +0x156a3d2e785604 +0x6de195e62fd9c +0x1 +0x357a4cb6e9746 +0x1ec3aa7ce66580 +0x2cd +0x66 +0x17ac30ce8ec31b +0x9497852492b84 +0xa +0x18ac792a689485 +0x3266d37f6731d +0x0 +0x158865b1697fc3 +0xb5ea2b426535e +0x6 +0x1db03d2d540db2 +0x48084744eebcc +0x1 +0xf03b281d81e80 +0x10b4af861d654f +0xe +0x1b54ecc34b53e2 +0x6106d8d99c88d +0x3 +0xb165bcd6cecde +0xf3ff59d242ab5 +0xf +0x340e867d1532d +0x1130568709d9e7 +0x36 +0x12d1ffb3754d5b +0xa84458eda2d0e +0x7 +0x1a64f2673adbc7 +0x18822fb55da60d +0x30c +0x230 +0x393 +0x5e +0x1249caf2fa4cc7 +0x1090841a6a3c7 +0x1 +0xbbab3008bcd4b +0x5e0cf2416db3c +0x1 +0x1ae89840ef2766 +0x8c200ba4313b8 +0x5 +0x1fee6a270f448d +0x11d30f6fb09a46 +0xce +0xe8381b8eda998 +0x1f1e4a7c6aa3ec +0xffffffffffffffff +0xf6462d88f7320 +0x49d09a27bb9f3 +0x2 +0x416e964435cca +0x1e1ff6f6b79794 +0x367 +0x2a1 +0x4e +0x2c8f3bf759265 +0xc76dc2ff2479b +0xb +0x1b474ddd26012d +0x18213ba0d029a5 +0x2c7 +0x21e +0x3cd +0x2ed +0x1c9 +0x12eeb282e67311 +0x70f801cad570 +0x0 +0xe7431464d3755 +0x1033f1de5f67a0 +0x58 +0x1535a55a6d00d +0xeec21f4e4462d +0x4 +0x74c5911718c5f +0xcde3a5429d3bf +0x8 +0x11b45fdd094ebb +0x1f167cee04ae61 +0xffffffffffffffff +0x17a28518b52445 +0x3321f47533362 +0x0 +0xdd01a125c4a4a +0x6a89a8d9ab90b +0x3 +0xe1049754f4420 +0x94c9cca847000 +0x2 +0x163568c0c286a3 +0x320875f898519 +0x1 +0xd65f1429c43f8 +0x19d7d707113edd +0x20f +0x2c9 +0x4f +0x860cd5aab781b +0x39b3c9375f97d +0x0 +0x2333e3d7ca98d +0x17a6d7616fae5 +0x0 +0x1be31c31fa1b65 +0x1e9cf0eff97714 +0x3b2 +0x1c5 +0x228d962252c1 +0x1da1342813d162 +0xe8 +0xcb52be9526514 +0xbe7715933fd26 +0x8 +0x8f1a4b72402b3 +0xd29274e087de2 +0xa +0x16eaf729826089 +0x18c0985d643ec9 +0x3eb +0x3af +0xef +0x771448beb2b60 +0xca48c76e6a43b +0x14 +0x12e8d395659fca +0x6962f0ecdd7eb +0x5 +0x1b5b9a7947347f +0x111eac80e7a6a +0x0 +0xa2f9593345b1a +0x23ab2574ece76 +0x0 +0x52bdb9f49038 +0x13c1e91774246c +0x109 +0x11678fb01f0e94 +0x1eed50c572ce61 +0x276 +0x96 +0x85293dd2b81d9 +0x46f5a55a32f21 +0x2 +0x8ea156b2403b8 +0x130c376c244b7a +0xea +0x593dbf28c8642 +0x48fb750754798 +0x0 +0x218b6e19d2ce7 +0x28667e2348614 +0x0 +0xb47c81a484822 +0xe7763abc2530e +0x3e +0x11621e6432f25e +0xb6e51a3c7540d +0x5 +0x15a8205db3e4cf +0x1f528a70225267 +0xffffffffffffffff +0x7f1a01494e0e2 +0x64a97840199f5 +0x0 +0xe8a7648610a60 +0x7814e573acea1 +0x2 +0x9f52042f72a1f +0xceeb054adc0f0 +0x11 +0x1f8e17539ad7cf +0x726edc3a875f6 +0x5 +0x14704edc0975f7 +0x118b2f49f75478 +0x4b +0x1bc318c2f02b8e +0x158d0ef586448e +0x2a1 +0x381 +0x136 +0x10bd503772dde5 +0x11f23f5e7b3f97 +0xac +0x184032fc1d0289 +0x112711dd817d83 +0x18 +0x273b20abf615c +0x1a1f72d887b0c2 +0x221 +0x58 +0x75447e86e9506 +0x1f5077c5158403 +0xffffffffffffffff +0x1484c44b895db2 +0x1a7a4cd63fe6fd +0x2d8 +0x3a7 +0x1be +0x49d80aad06826 +0xb75b3c47ca70b +0x7 +0x17b50ef6547c08 +0x61a34e6ccfe60 +0x3 +0x17c0e7eed36e10 +0x6a094e5ea11f2 +0x3 +0xe6e5d341e0f9f +0x18519b4701487e +0x3fb +0x72 +0xf64409ba1a52b +0x7c54c8de9c737 +0x6 +0x3bd3309d85dfa +0xd7c26e0c06e7f +0x2 +0x10a3874da0fe24 +0x17ee1a5fd6e736 +0x367 +0x25e +0x1aa +0xedea7935cd0ab +0xbce43cf730e22 +0xd +0x189100661f2b80 +0xbf03d117400cd +0xd +0x1940aba500afec +0xf0331a24e3663 +0x6 +0xbf51d6f2c634e +0x135a22a6c9173a +0x1d +0x6082f4e73edc4 +0x1e76c6b2e83524 +0x82 +0x23d8833c0cfd1 +0xad6c5e891d077 +0xf +0x1395753897997a +0x12a341871f9b19 +0xc4 +0x1af8256ea1b399 +0x1e023f88c2f3d1 +0x21f +0x1de +0x15e381df69a2fb +0x36dbfaad03710 +0x0 +0x1636516372e7b1 +0x2f1ee347aed69 +0x1 +0xb3da16c4564fc +0xc3cbf6936777d +0xa +0xa747baaa63b10 +0x5fd09cf3fd7ae +0x3 +0x841b987647c93 +0x14ee7c5e1c4aec +0x378 +0x310 +0x196 +0x86138cb44b38e +0x11ce7c42224c97 +0x91 +0x199672e5f8fe30 +0x19f254a45c1ad8 +0xf1 +0x93cbdc9747fab +0xd9f690e1b64d0 +0x0 +0x1ab1e9f7ba4a +0x59e57af077bea +0x2 +0x1c885e18521117 +0x81245406d707c +0x5 +0x1c09c2947a1067 +0x13215075ebb743 +0xe5 +0x1fc0b792b6c8ce +0xe9884cf04721d +0x2f +0x144617c620a489 +0x1f497e7115f5b4 +0xffffffffffffffff +0x1ae47ced5ad3a3 +0xa2c6fbd42a883 +0x5 +0xb9e9253823be +0x1bd67da246b7a6 +0x10e +0x1bb16c7f546078 +0x1edc5424565bbe +0x21c +0x305 +0x309 +0x94 +0x72cc716d0d736 +0x1e7c78aceae3e8 +0x124 +0xd0bf7d6644188 +0x133d77cb1a83e7 +0x92 +0x139da06e6cbf55 +0x1ca3de51b16a13 +0x19b +0x194a80ee239485 +0x1704d2bf665acd +0x3f4 +0x229 +0x31 +0x30bb2986d48e0 +0x6dad9af00bae1 +0x3 +0x143d3ccf72cc6a +0x1272257645e054 +0x84 +0x1124a37048f083 +0x1084ce7caad86a +0x9 +0x1cea54a09c4bb5 +0x1ab6eac00ea1cc +0xc1 +0x13c855d1666b97 +0x1da18afcac568a +0x273 +0x1fe +0x134dcf198d08d5 +0x56d7aa877e65b +0x3 +0x19c1af5ea21ea0 +0xeaca78e7822 +0x1 +0x807e8630dc7ed +0xdb91f876d1fe5 +0x16 +0xdd5c5f42f155 +0x129a02e8915eef +0x2 +0x365fdd52f9d57 +0x15660795ef0ed4 +0x217 +0x88 +0xdc1d746a07de2 +0x1e4a5c76a57b57 +0x198 +0x13b94df18444f1 +0xbb1f76a846da0 +0xb +0x17bb74a9f969e6 +0x42d5523f10d7a +0x1 +0x105046a138d532 +0x30621e8844265 +0x1 \ No newline at end of file diff --git a/core/transcoder_test.go b/core/transcoder_test.go index 8b8061ffc..25e19c581 100644 --- a/core/transcoder_test.go +++ b/core/transcoder_test.go @@ -32,10 +32,10 @@ func TestLocalTranscoder(t *testing.T) { if len(res.Segments) != len(videoProfiles) { t.Error("Mismatched results") } - if Over1Pct(len(res.Segments[0].Data), 522264) { + if Over1Pct(len(res.Segments[0].Data), 585620) { t.Errorf("Wrong data %v", len(res.Segments[0].Data)) } - if Over1Pct(len(res.Segments[1].Data), 715528) { + if Over1Pct(len(res.Segments[1].Data), 813100) { t.Errorf("Wrong data %v", len(res.Segments[1].Data)) } } diff --git a/discovery/db_discovery.go b/discovery/db_discovery.go index caf210666..5c9905bdd 100644 --- a/discovery/db_discovery.go +++ b/discovery/db_discovery.go @@ -16,7 +16,6 @@ import ( lpTypes "github.com/livepeer/go-livepeer/eth/types" "github.com/livepeer/go-livepeer/net" "github.com/livepeer/go-livepeer/pm" - "github.com/livepeer/go-livepeer/server" "github.com/golang/glog" ) @@ -71,7 +70,6 @@ func NewDBOrchestratorPoolCache(ctx context.Context, node *core.LivepeerNode, rm func (dbo *DBOrchestratorPoolCache) getURLs() ([]*url.URL, error) { orchs, err := dbo.store.SelectOrchs( &common.DBOrchFilter{ - MaxPrice: server.BroadcastCfg.MaxPrice(), CurrentRound: dbo.rm.LastInitializedRound(), UpdatedLastDay: true, }, @@ -120,8 +118,7 @@ func (dbo *DBOrchestratorPoolCache) GetOrchestrators(ctx context.Context, numOrc return false } - // check if O's price is below B's max price - maxPrice := server.BroadcastCfg.MaxPrice() + // check if O has a valid price price, err := common.RatPriceInfo(info.PriceInfo) if err != nil { clog.V(common.DEBUG).Infof(ctx, "invalid price info orch=%v err=%q", info.GetTranscoder(), err) @@ -131,12 +128,8 @@ func (dbo *DBOrchestratorPoolCache) GetOrchestrators(ctx context.Context, numOrc clog.V(common.DEBUG).Infof(ctx, "no price info received for orch=%v", info.GetTranscoder()) return false } - if maxPrice != nil && price.Cmp(maxPrice) > 0 { - clog.V(common.DEBUG).Infof(ctx, "orchestrator's price is too high orch=%v price=%v wei/pixel maxPrice=%v wei/pixel", - info.GetTranscoder(), - price.FloatString(3), - maxPrice.FloatString(3), - ) + if price.Sign() < 0 { + clog.V(common.DEBUG).Infof(ctx, "invalid price received for orch=%v price=%v", info.GetTranscoder(), price.RatString()) return false } return true @@ -154,7 +147,6 @@ func (dbo *DBOrchestratorPoolCache) GetOrchestrators(ctx context.Context, numOrc func (dbo *DBOrchestratorPoolCache) Size() int { count, _ := dbo.store.OrchCount( &common.DBOrchFilter{ - MaxPrice: server.BroadcastCfg.MaxPrice(), CurrentRound: dbo.rm.LastInitializedRound(), UpdatedLastDay: true, }, diff --git a/discovery/discovery_test.go b/discovery/discovery_test.go index c52ce7ce6..33e20e5b7 100644 --- a/discovery/discovery_test.go +++ b/discovery/discovery_test.go @@ -608,11 +608,11 @@ func TestNewOrchestratorPoolWithPred_TestPredicate(t *testing.T) { assert.True(t, pool.pred(oInfo)) // Set server.BroadcastCfg.maxPrice higher than PriceInfo , should return true - server.BroadcastCfg.SetMaxPrice(big.NewRat(10, 1)) + server.BroadcastCfg.SetMaxPrice(core.NewFixedPrice(big.NewRat(10, 1))) assert.True(t, pool.pred(oInfo)) // Set MaxBroadcastPrice lower than PriceInfo, should return false - server.BroadcastCfg.SetMaxPrice(big.NewRat(1, 1)) + server.BroadcastCfg.SetMaxPrice(core.NewFixedPrice(big.NewRat(1, 1))) assert.False(t, pool.pred(oInfo)) // PixelsPerUnit is 0 , return false @@ -620,7 +620,7 @@ func TestNewOrchestratorPoolWithPred_TestPredicate(t *testing.T) { assert.False(t, pool.pred(oInfo)) } -func TestCachedPool_AllOrchestratorsTooExpensive_ReturnsEmptyList(t *testing.T) { +func TestCachedPool_AllOrchestratorsTooExpensive_ReturnsAllOrchestrators(t *testing.T) { // Test setup expPriceInfo := &net.PriceInfo{ PricePerUnit: 999, @@ -629,7 +629,7 @@ func TestCachedPool_AllOrchestratorsTooExpensive_ReturnsEmptyList(t *testing.T) expTranscoder := "transcoderFromTest" expPricePerPixel, _ := common.PriceToFixed(big.NewRat(999, 1)) - server.BroadcastCfg.SetMaxPrice(big.NewRat(1, 1)) + server.BroadcastCfg.SetMaxPrice(core.NewFixedPrice(big.NewRat(1, 1))) gmp := runtime.GOMAXPROCS(50) defer runtime.GOMAXPROCS(gmp) var mu sync.Mutex @@ -698,14 +698,15 @@ func TestCachedPool_AllOrchestratorsTooExpensive_ReturnsEmptyList(t *testing.T) } // check size - assert.Equal(0, pool.Size()) + assert.Equal(50, pool.Size()) urls := pool.GetInfos() - assert.Len(urls, 0) + assert.Len(urls, 50) + infos, err := pool.GetOrchestrators(context.TODO(), len(addresses), newStubSuspender(), newStubCapabilities(), common.ScoreAtLeast(0)) assert.Nil(err, "Should not be error") - assert.Len(infos, 0) + assert.Len(infos, 50) } func TestCachedPool_GetOrchestrators_MaxBroadcastPriceNotSet(t *testing.T) { @@ -823,7 +824,7 @@ func TestCachedPool_N_OrchestratorsGoodPricing_ReturnsNOrchestrators(t *testing. }, } - server.BroadcastCfg.SetMaxPrice(big.NewRat(10, 1)) + server.BroadcastCfg.SetMaxPrice(core.NewFixedPrice(big.NewRat(10, 1))) gmp := runtime.GOMAXPROCS(50) defer runtime.GOMAXPROCS(gmp) var mu sync.Mutex @@ -899,22 +900,27 @@ func TestCachedPool_N_OrchestratorsGoodPricing_ReturnsNOrchestrators(t *testing. assert.Contains(testOrchs[25:], toOrchTest(o.EthereumAddr, o.ServiceURI, o.PricePerPixel)) } - // check size - assert.Equal(25, pool.Size()) + // check pool returns all Os, not filtering by max price + assert.Equal(50, pool.Size()) infos := pool.GetInfos() - assert.Len(infos, 25) + assert.Len(infos, 50) for _, info := range infos { - assert.Contains(addresses[25:], info.URL.String()) + assert.Contains(addresses, info.URL.String()) } oinfos, err := pool.GetOrchestrators(context.TODO(), len(orchestrators), newStubSuspender(), newStubCapabilities(), common.ScoreAtLeast(0)) assert.Nil(err, "Should not be error") - assert.Len(oinfos, 25) + assert.Len(oinfos, 50) + + seenAddrs := make(map[string]bool) for _, info := range oinfos { - assert.Equal(info.RemoteInfo.Transcoder, "goodPriceTranscoder") + addr := info.LocalInfo.URL.String() + assert.Contains(addresses, addr) + seenAddrs[addr] = true } + assert.Len(seenAddrs, 50) } func TestCachedPool_GetOrchestrators_TicketParamsValidation(t *testing.T) { diff --git a/eth/contracts/chainlink/AggregatorV3Interface.abi b/eth/contracts/chainlink/AggregatorV3Interface.abi new file mode 100644 index 000000000..106c4a7bc --- /dev/null +++ b/eth/contracts/chainlink/AggregatorV3Interface.abi @@ -0,0 +1 @@ +[{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"description","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint80","name":"_roundId","type":"uint80"}],"name":"getRoundData","outputs":[{"internalType":"uint80","name":"roundId","type":"uint80"},{"internalType":"int256","name":"answer","type":"int256"},{"internalType":"uint256","name":"startedAt","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"},{"internalType":"uint80","name":"answeredInRound","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestRoundData","outputs":[{"internalType":"uint80","name":"roundId","type":"uint80"},{"internalType":"int256","name":"answer","type":"int256"},{"internalType":"uint256","name":"startedAt","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"},{"internalType":"uint80","name":"answeredInRound","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}] diff --git a/eth/contracts/chainlink/AggregatorV3Interface.go b/eth/contracts/chainlink/AggregatorV3Interface.go new file mode 100644 index 000000000..2b0c1c958 --- /dev/null +++ b/eth/contracts/chainlink/AggregatorV3Interface.go @@ -0,0 +1,394 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package chainlink + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// AggregatorV3InterfaceMetaData contains all meta data concerning the AggregatorV3Interface contract. +var AggregatorV3InterfaceMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"description\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint80\",\"name\":\"_roundId\",\"type\":\"uint80\"}],\"name\":\"getRoundData\",\"outputs\":[{\"internalType\":\"uint80\",\"name\":\"roundId\",\"type\":\"uint80\"},{\"internalType\":\"int256\",\"name\":\"answer\",\"type\":\"int256\"},{\"internalType\":\"uint256\",\"name\":\"startedAt\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"updatedAt\",\"type\":\"uint256\"},{\"internalType\":\"uint80\",\"name\":\"answeredInRound\",\"type\":\"uint80\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestRoundData\",\"outputs\":[{\"internalType\":\"uint80\",\"name\":\"roundId\",\"type\":\"uint80\"},{\"internalType\":\"int256\",\"name\":\"answer\",\"type\":\"int256\"},{\"internalType\":\"uint256\",\"name\":\"startedAt\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"updatedAt\",\"type\":\"uint256\"},{\"internalType\":\"uint80\",\"name\":\"answeredInRound\",\"type\":\"uint80\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"version\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", +} + +// AggregatorV3InterfaceABI is the input ABI used to generate the binding from. +// Deprecated: Use AggregatorV3InterfaceMetaData.ABI instead. +var AggregatorV3InterfaceABI = AggregatorV3InterfaceMetaData.ABI + +// AggregatorV3Interface is an auto generated Go binding around an Ethereum contract. +type AggregatorV3Interface struct { + AggregatorV3InterfaceCaller // Read-only binding to the contract + AggregatorV3InterfaceTransactor // Write-only binding to the contract + AggregatorV3InterfaceFilterer // Log filterer for contract events +} + +// AggregatorV3InterfaceCaller is an auto generated read-only Go binding around an Ethereum contract. +type AggregatorV3InterfaceCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// AggregatorV3InterfaceTransactor is an auto generated write-only Go binding around an Ethereum contract. +type AggregatorV3InterfaceTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// AggregatorV3InterfaceFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type AggregatorV3InterfaceFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// AggregatorV3InterfaceSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type AggregatorV3InterfaceSession struct { + Contract *AggregatorV3Interface // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// AggregatorV3InterfaceCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type AggregatorV3InterfaceCallerSession struct { + Contract *AggregatorV3InterfaceCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// AggregatorV3InterfaceTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type AggregatorV3InterfaceTransactorSession struct { + Contract *AggregatorV3InterfaceTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// AggregatorV3InterfaceRaw is an auto generated low-level Go binding around an Ethereum contract. +type AggregatorV3InterfaceRaw struct { + Contract *AggregatorV3Interface // Generic contract binding to access the raw methods on +} + +// AggregatorV3InterfaceCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type AggregatorV3InterfaceCallerRaw struct { + Contract *AggregatorV3InterfaceCaller // Generic read-only contract binding to access the raw methods on +} + +// AggregatorV3InterfaceTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type AggregatorV3InterfaceTransactorRaw struct { + Contract *AggregatorV3InterfaceTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewAggregatorV3Interface creates a new instance of AggregatorV3Interface, bound to a specific deployed contract. +func NewAggregatorV3Interface(address common.Address, backend bind.ContractBackend) (*AggregatorV3Interface, error) { + contract, err := bindAggregatorV3Interface(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &AggregatorV3Interface{AggregatorV3InterfaceCaller: AggregatorV3InterfaceCaller{contract: contract}, AggregatorV3InterfaceTransactor: AggregatorV3InterfaceTransactor{contract: contract}, AggregatorV3InterfaceFilterer: AggregatorV3InterfaceFilterer{contract: contract}}, nil +} + +// NewAggregatorV3InterfaceCaller creates a new read-only instance of AggregatorV3Interface, bound to a specific deployed contract. +func NewAggregatorV3InterfaceCaller(address common.Address, caller bind.ContractCaller) (*AggregatorV3InterfaceCaller, error) { + contract, err := bindAggregatorV3Interface(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &AggregatorV3InterfaceCaller{contract: contract}, nil +} + +// NewAggregatorV3InterfaceTransactor creates a new write-only instance of AggregatorV3Interface, bound to a specific deployed contract. +func NewAggregatorV3InterfaceTransactor(address common.Address, transactor bind.ContractTransactor) (*AggregatorV3InterfaceTransactor, error) { + contract, err := bindAggregatorV3Interface(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &AggregatorV3InterfaceTransactor{contract: contract}, nil +} + +// NewAggregatorV3InterfaceFilterer creates a new log filterer instance of AggregatorV3Interface, bound to a specific deployed contract. +func NewAggregatorV3InterfaceFilterer(address common.Address, filterer bind.ContractFilterer) (*AggregatorV3InterfaceFilterer, error) { + contract, err := bindAggregatorV3Interface(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &AggregatorV3InterfaceFilterer{contract: contract}, nil +} + +// bindAggregatorV3Interface binds a generic wrapper to an already deployed contract. +func bindAggregatorV3Interface(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := AggregatorV3InterfaceMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_AggregatorV3Interface *AggregatorV3InterfaceRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _AggregatorV3Interface.Contract.AggregatorV3InterfaceCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_AggregatorV3Interface *AggregatorV3InterfaceRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _AggregatorV3Interface.Contract.AggregatorV3InterfaceTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_AggregatorV3Interface *AggregatorV3InterfaceRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _AggregatorV3Interface.Contract.AggregatorV3InterfaceTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_AggregatorV3Interface *AggregatorV3InterfaceCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _AggregatorV3Interface.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_AggregatorV3Interface *AggregatorV3InterfaceTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _AggregatorV3Interface.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_AggregatorV3Interface *AggregatorV3InterfaceTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _AggregatorV3Interface.Contract.contract.Transact(opts, method, params...) +} + +// Decimals is a free data retrieval call binding the contract method 0x313ce567. +// +// Solidity: function decimals() view returns(uint8) +func (_AggregatorV3Interface *AggregatorV3InterfaceCaller) Decimals(opts *bind.CallOpts) (uint8, error) { + var out []interface{} + err := _AggregatorV3Interface.contract.Call(opts, &out, "decimals") + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +// Decimals is a free data retrieval call binding the contract method 0x313ce567. +// +// Solidity: function decimals() view returns(uint8) +func (_AggregatorV3Interface *AggregatorV3InterfaceSession) Decimals() (uint8, error) { + return _AggregatorV3Interface.Contract.Decimals(&_AggregatorV3Interface.CallOpts) +} + +// Decimals is a free data retrieval call binding the contract method 0x313ce567. +// +// Solidity: function decimals() view returns(uint8) +func (_AggregatorV3Interface *AggregatorV3InterfaceCallerSession) Decimals() (uint8, error) { + return _AggregatorV3Interface.Contract.Decimals(&_AggregatorV3Interface.CallOpts) +} + +// Description is a free data retrieval call binding the contract method 0x7284e416. +// +// Solidity: function description() view returns(string) +func (_AggregatorV3Interface *AggregatorV3InterfaceCaller) Description(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _AggregatorV3Interface.contract.Call(opts, &out, "description") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// Description is a free data retrieval call binding the contract method 0x7284e416. +// +// Solidity: function description() view returns(string) +func (_AggregatorV3Interface *AggregatorV3InterfaceSession) Description() (string, error) { + return _AggregatorV3Interface.Contract.Description(&_AggregatorV3Interface.CallOpts) +} + +// Description is a free data retrieval call binding the contract method 0x7284e416. +// +// Solidity: function description() view returns(string) +func (_AggregatorV3Interface *AggregatorV3InterfaceCallerSession) Description() (string, error) { + return _AggregatorV3Interface.Contract.Description(&_AggregatorV3Interface.CallOpts) +} + +// GetRoundData is a free data retrieval call binding the contract method 0x9a6fc8f5. +// +// Solidity: function getRoundData(uint80 _roundId) view returns(uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) +func (_AggregatorV3Interface *AggregatorV3InterfaceCaller) GetRoundData(opts *bind.CallOpts, _roundId *big.Int) (struct { + RoundId *big.Int + Answer *big.Int + StartedAt *big.Int + UpdatedAt *big.Int + AnsweredInRound *big.Int +}, error) { + var out []interface{} + err := _AggregatorV3Interface.contract.Call(opts, &out, "getRoundData", _roundId) + + outstruct := new(struct { + RoundId *big.Int + Answer *big.Int + StartedAt *big.Int + UpdatedAt *big.Int + AnsweredInRound *big.Int + }) + if err != nil { + return *outstruct, err + } + + outstruct.RoundId = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + outstruct.Answer = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int) + outstruct.StartedAt = *abi.ConvertType(out[2], new(*big.Int)).(**big.Int) + outstruct.UpdatedAt = *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + outstruct.AnsweredInRound = *abi.ConvertType(out[4], new(*big.Int)).(**big.Int) + + return *outstruct, err + +} + +// GetRoundData is a free data retrieval call binding the contract method 0x9a6fc8f5. +// +// Solidity: function getRoundData(uint80 _roundId) view returns(uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) +func (_AggregatorV3Interface *AggregatorV3InterfaceSession) GetRoundData(_roundId *big.Int) (struct { + RoundId *big.Int + Answer *big.Int + StartedAt *big.Int + UpdatedAt *big.Int + AnsweredInRound *big.Int +}, error) { + return _AggregatorV3Interface.Contract.GetRoundData(&_AggregatorV3Interface.CallOpts, _roundId) +} + +// GetRoundData is a free data retrieval call binding the contract method 0x9a6fc8f5. +// +// Solidity: function getRoundData(uint80 _roundId) view returns(uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) +func (_AggregatorV3Interface *AggregatorV3InterfaceCallerSession) GetRoundData(_roundId *big.Int) (struct { + RoundId *big.Int + Answer *big.Int + StartedAt *big.Int + UpdatedAt *big.Int + AnsweredInRound *big.Int +}, error) { + return _AggregatorV3Interface.Contract.GetRoundData(&_AggregatorV3Interface.CallOpts, _roundId) +} + +// LatestRoundData is a free data retrieval call binding the contract method 0xfeaf968c. +// +// Solidity: function latestRoundData() view returns(uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) +func (_AggregatorV3Interface *AggregatorV3InterfaceCaller) LatestRoundData(opts *bind.CallOpts) (struct { + RoundId *big.Int + Answer *big.Int + StartedAt *big.Int + UpdatedAt *big.Int + AnsweredInRound *big.Int +}, error) { + var out []interface{} + err := _AggregatorV3Interface.contract.Call(opts, &out, "latestRoundData") + + outstruct := new(struct { + RoundId *big.Int + Answer *big.Int + StartedAt *big.Int + UpdatedAt *big.Int + AnsweredInRound *big.Int + }) + if err != nil { + return *outstruct, err + } + + outstruct.RoundId = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + outstruct.Answer = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int) + outstruct.StartedAt = *abi.ConvertType(out[2], new(*big.Int)).(**big.Int) + outstruct.UpdatedAt = *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + outstruct.AnsweredInRound = *abi.ConvertType(out[4], new(*big.Int)).(**big.Int) + + return *outstruct, err + +} + +// LatestRoundData is a free data retrieval call binding the contract method 0xfeaf968c. +// +// Solidity: function latestRoundData() view returns(uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) +func (_AggregatorV3Interface *AggregatorV3InterfaceSession) LatestRoundData() (struct { + RoundId *big.Int + Answer *big.Int + StartedAt *big.Int + UpdatedAt *big.Int + AnsweredInRound *big.Int +}, error) { + return _AggregatorV3Interface.Contract.LatestRoundData(&_AggregatorV3Interface.CallOpts) +} + +// LatestRoundData is a free data retrieval call binding the contract method 0xfeaf968c. +// +// Solidity: function latestRoundData() view returns(uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) +func (_AggregatorV3Interface *AggregatorV3InterfaceCallerSession) LatestRoundData() (struct { + RoundId *big.Int + Answer *big.Int + StartedAt *big.Int + UpdatedAt *big.Int + AnsweredInRound *big.Int +}, error) { + return _AggregatorV3Interface.Contract.LatestRoundData(&_AggregatorV3Interface.CallOpts) +} + +// Version is a free data retrieval call binding the contract method 0x54fd4d50. +// +// Solidity: function version() view returns(uint256) +func (_AggregatorV3Interface *AggregatorV3InterfaceCaller) Version(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _AggregatorV3Interface.contract.Call(opts, &out, "version") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Version is a free data retrieval call binding the contract method 0x54fd4d50. +// +// Solidity: function version() view returns(uint256) +func (_AggregatorV3Interface *AggregatorV3InterfaceSession) Version() (*big.Int, error) { + return _AggregatorV3Interface.Contract.Version(&_AggregatorV3Interface.CallOpts) +} + +// Version is a free data retrieval call binding the contract method 0x54fd4d50. +// +// Solidity: function version() view returns(uint256) +func (_AggregatorV3Interface *AggregatorV3InterfaceCallerSession) Version() (*big.Int, error) { + return _AggregatorV3Interface.Contract.Version(&_AggregatorV3Interface.CallOpts) +} diff --git a/eth/contracts/chainlink/AggregatorV3Interface.sol b/eth/contracts/chainlink/AggregatorV3Interface.sol new file mode 100644 index 000000000..1bedfce21 --- /dev/null +++ b/eth/contracts/chainlink/AggregatorV3Interface.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +// https://github.com/smartcontractkit/chainlink/blob/v2.9.1/contracts/src/v0.7/interfaces/AggregatorV3Interface.sol +pragma solidity ^0.7.0; + +interface AggregatorV3Interface { + function decimals() external view returns (uint8); + + function description() external view returns (string memory); + + function version() external view returns (uint256); + + // getRoundData and latestRoundData should both raise "No data present" + // if they do not have data to report, instead of returning unset values + // which could be misinterpreted as actual reported values. + function getRoundData(uint80 _roundId) + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + + function latestRoundData() + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); +} diff --git a/eth/pricefeed.go b/eth/pricefeed.go new file mode 100644 index 000000000..a9f51d012 --- /dev/null +++ b/eth/pricefeed.go @@ -0,0 +1,78 @@ +package eth + +import ( + "errors" + "fmt" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/livepeer/go-livepeer/eth/contracts/chainlink" +) + +type PriceData struct { + RoundID int64 + Price *big.Rat + UpdatedAt time.Time +} + +// PriceFeedEthClient is an interface for fetching price data from a Chainlink +// PriceFeed contract. +type PriceFeedEthClient interface { + Description() (string, error) + FetchPriceData() (PriceData, error) +} + +func NewPriceFeedEthClient(ethClient *ethclient.Client, priceFeedAddr string) (PriceFeedEthClient, error) { + addr := common.HexToAddress(priceFeedAddr) + priceFeed, err := chainlink.NewAggregatorV3Interface(addr, ethClient) + if err != nil { + return nil, fmt.Errorf("failed to create aggregator proxy: %w", err) + } + + return &priceFeedClient{ + client: ethClient, + priceFeed: priceFeed, + }, nil +} + +type priceFeedClient struct { + client *ethclient.Client + priceFeed *chainlink.AggregatorV3Interface +} + +func (c *priceFeedClient) Description() (string, error) { + return c.priceFeed.Description(&bind.CallOpts{}) +} + +func (c *priceFeedClient) FetchPriceData() (PriceData, error) { + data, err := c.priceFeed.LatestRoundData(&bind.CallOpts{}) + if err != nil { + return PriceData{}, errors.New("failed to get latest round data: " + err.Error()) + } + + decimals, err := c.priceFeed.Decimals(&bind.CallOpts{}) + if err != nil { + return PriceData{}, errors.New("failed to get decimals: " + err.Error()) + } + + return computePriceData(data.RoundId, data.UpdatedAt, data.Answer, decimals), nil +} + +// computePriceData transforms the raw data from the PriceFeed into the higher +// level PriceData struct, more easily usable by the rest of the system. +func computePriceData(roundID, updatedAt, answer *big.Int, decimals uint8) PriceData { + // Compute a big.int which is 10^decimals. + divisor := new(big.Int).Exp( + big.NewInt(10), + big.NewInt(int64(decimals)), + nil) + + return PriceData{ + RoundID: roundID.Int64(), + Price: new(big.Rat).SetFrac(answer, divisor), + UpdatedAt: time.Unix(updatedAt.Int64(), 0), + } +} diff --git a/eth/pricefeed_test.go b/eth/pricefeed_test.go new file mode 100644 index 000000000..981a158e7 --- /dev/null +++ b/eth/pricefeed_test.go @@ -0,0 +1,51 @@ +package eth + +import ( + "math/big" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestComputePriceData(t *testing.T) { + assert := assert.New(t) + + t.Run("valid data", func(t *testing.T) { + roundID := big.NewInt(1) + updatedAt := big.NewInt(1626192000) + answer := big.NewInt(420666000) + decimals := uint8(6) + + data := computePriceData(roundID, updatedAt, answer, decimals) + + assert.EqualValues(int64(1), data.RoundID, "Round ID didn't match") + assert.Equal("210333/500", data.Price.RatString(), "The Price Rat didn't match") + assert.Equal("2021-07-13 16:00:00 +0000 UTC", data.UpdatedAt.UTC().String(), "The updated at time did not match") + }) + + t.Run("zero answer", func(t *testing.T) { + roundID := big.NewInt(2) + updatedAt := big.NewInt(1626192000) + answer := big.NewInt(0) + decimals := uint8(18) + + data := computePriceData(roundID, updatedAt, answer, decimals) + + assert.EqualValues(int64(2), data.RoundID, "Round ID didn't match") + assert.Equal("0", data.Price.RatString(), "The Price Rat didn't match") + assert.Equal("2021-07-13 16:00:00 +0000 UTC", data.UpdatedAt.UTC().String(), "The updated at time did not match") + }) + + t.Run("zero decimals", func(t *testing.T) { + roundID := big.NewInt(3) + updatedAt := big.NewInt(1626192000) + answer := big.NewInt(13) + decimals := uint8(0) + + data := computePriceData(roundID, updatedAt, answer, decimals) + + assert.EqualValues(int64(3), data.RoundID, "Round ID didn't match") + assert.Equal("13", data.Price.RatString(), "The Price Rat didn't match") + assert.Equal("2021-07-13 16:00:00 +0000 UTC", data.UpdatedAt.UTC().String(), "The updated at time did not match") + }) +} diff --git a/eth/roundinitializer.go b/eth/roundinitializer.go index d3f2cbeee..a7aaff666 100644 --- a/eth/roundinitializer.go +++ b/eth/roundinitializer.go @@ -2,10 +2,11 @@ package eth import ( "math/big" + "math/rand" "sync" + "time" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/event" "github.com/golang/glog" ) @@ -29,20 +30,22 @@ type timeWatcher interface { // This selection process is purely a client side implementation that attempts to minimize on-chain transaction collisions, but // collisions are still possible if initialization transactions are submitted by parties that are not using this selection process type RoundInitializer struct { - client LivepeerEthClient - tw timeWatcher - quit chan struct{} + maxDelay time.Duration + client LivepeerEthClient + tw timeWatcher + quit chan struct{} nextRoundStartL1Block *big.Int mu sync.Mutex } // NewRoundInitializer creates a RoundInitializer instance -func NewRoundInitializer(client LivepeerEthClient, tw timeWatcher) *RoundInitializer { +func NewRoundInitializer(client LivepeerEthClient, tw timeWatcher, maxDelay time.Duration) *RoundInitializer { return &RoundInitializer{ - client: client, - tw: tw, - quit: make(chan struct{}), + maxDelay: maxDelay, + client: client, + tw: tw, + quit: make(chan struct{}), } } @@ -104,23 +107,23 @@ func (r *RoundInitializer) tryInitialize() error { r.mu.Lock() defer r.mu.Unlock() - currentL1Blk := r.tw.LastSeenL1Block() - lastInitializedL1BlkHash := r.tw.LastInitializedL1BlockHash() - - epochSeed := r.currentEpochSeed(currentL1Blk, r.nextRoundStartL1Block, lastInitializedL1BlkHash) - - ok, err := r.shouldInitialize(epochSeed) - if err != nil { - return err + if r.tw.LastSeenL1Block().Cmp(r.nextRoundStartL1Block) < 0 { + // Round already initialized + return nil } - // Noop if the caller should not initialize the round - if !ok { - return nil + if r.maxDelay > 0 { + randDelay := time.Duration(rand.Int63n(int64(r.maxDelay))) + glog.Infof("Waiting %v before attempting to initialize round", randDelay) + time.Sleep(randDelay) + + if r.tw.LastSeenL1Block().Cmp(r.nextRoundStartL1Block) < 0 { + glog.Infof("Round is already initialized, not initializing") + return nil + } } currentRound := new(big.Int).Add(r.tw.LastInitializedRound(), big.NewInt(1)) - glog.Infof("New round - preparing to initialize round to join active set, current round is %d", currentRound) tx, err := r.client.InitializeRound() @@ -136,55 +139,3 @@ func (r *RoundInitializer) tryInitialize() error { return nil } - -func (r *RoundInitializer) shouldInitialize(epochSeed *big.Int) (bool, error) { - transcoders, err := r.client.TranscoderPool() - if err != nil { - return false, err - } - - numActive := big.NewInt(int64(len(transcoders))) - - // Should not initialize if the upcoming active set is empty - if numActive.Cmp(big.NewInt(0)) == 0 { - return false, nil - } - - // Find the caller's rank in the upcoming active set - rank := int64(-1) - maxRank := numActive.Int64() - caller := r.client.Account().Address - for i := int64(0); i < maxRank; i++ { - if transcoders[i].Address == caller { - rank = i - break - } - } - - // Should not initialize if the caller is not in the upcoming active set - if rank == -1 { - return false, nil - } - - // Use the seed to select a position within the active set - selection := new(big.Int).Mod(epochSeed, numActive) - // Should not initialize if the selection does not match the caller's rank in the active set - if selection.Int64() != int64(rank) { - return false, nil - } - - // If the selection matches the caller's rank the caller should initialize the round - return true, nil -} - -// Returns the seed used to select a round initializer in the current epoch for the current round -// This seed is not meant to be unpredictable. The only requirement for the seed is that it is calculated the same way for each -// party running the round initializer -func (r *RoundInitializer) currentEpochSeed(currentL1Block, roundStartL1Block *big.Int, lastInitializedL1BlkHash [32]byte) *big.Int { - epochNum := new(big.Int).Sub(currentL1Block, roundStartL1Block) - epochNum.Div(epochNum, epochL1Blocks) - - // The seed for the current epoch is calculated as: - // keccak256(lastInitializedL1BlkHash | epochNum) - return crypto.Keccak256Hash(append(lastInitializedL1BlkHash[:], epochNum.Bytes()...)).Big() -} diff --git a/eth/roundinitializer_test.go b/eth/roundinitializer_test.go index 25e60205a..a96cb3b2f 100644 --- a/eth/roundinitializer_test.go +++ b/eth/roundinitializer_test.go @@ -16,84 +16,6 @@ import ( "github.com/stretchr/testify/mock" ) -func TestRoundInitializer_CurrentEpochSeed(t *testing.T) { - initializer := NewRoundInitializer(nil, nil) - - assert := assert.New(t) - - // Test epochNum = 0 - blkHash := [32]byte{123} - - epochSeed := initializer.currentEpochSeed(big.NewInt(5), big.NewInt(5), blkHash) - // epochNum = (5 - 5) / 5 = 0 - // epochSeed = keccak256(blkHash | 0) = 53205358842179480591542570540016728811976439286094436690881169143335261643310 - expEpochSeed, _ := new(big.Int).SetString("53205358842179480591542570540016728811976439286094436690881169143335261643310", 10) - assert.Equal(expEpochSeed, epochSeed) - - // Test epochNum > 0 - epochSeed = initializer.currentEpochSeed(big.NewInt(20), big.NewInt(5), blkHash) - // epochNum = (20 - 5) / 5 = 3 - // epochSeed = keccak256(blkHash | 3) = 42541119854153860846042329644941941146216657514071318786342840580076059276721 - expEpochSeed.SetString("42541119854153860846042329644941941146216657514071318786342840580076059276721", 10) - assert.Equal(expEpochSeed, epochSeed) - - // Test epochNum > 0 with some # of blocks into the epoch - epochSeed = initializer.currentEpochSeed(big.NewInt(20), big.NewInt(4), blkHash) - // epochNum = (20 - 4) / 5 = 3.2 -> 3 - assert.Equal(expEpochSeed, epochSeed) -} - -func TestRoundInitializer_ShouldInitialize(t *testing.T) { - client := &MockClient{} - tw := &stubTimeWatcher{} - initializer := NewRoundInitializer(client, tw) - - assert := assert.New(t) - - // Test error getting transcoders - expErr := errors.New("TranscoderPool error") - client.On("TranscoderPool").Return(nil, expErr).Once() - - ok, err := initializer.shouldInitialize(nil) - assert.EqualError(err, expErr.Error()) - assert.False(ok) - - // Test active set is empty because no registered transcoders - client.On("TranscoderPool").Return([]*lpTypes.Transcoder{}, nil).Once() - ok, err = initializer.shouldInitialize(nil) - assert.Nil(err) - assert.False(ok) - - // Test that caller is not in active set because it is not registered - caller := ethcommon.BytesToAddress([]byte("foo")) - client.On("Account").Return(accounts.Account{Address: caller}) - - registered := []*lpTypes.Transcoder{ - {Address: ethcommon.BytesToAddress([]byte("jar"))}, - {Address: ethcommon.BytesToAddress([]byte("bar"))}, - } - client.On("TranscoderPool").Return(registered, nil).Once() - - ok, err = initializer.shouldInitialize(nil) - assert.Nil(err) - assert.False(ok) - - // Test not selected - registered = append(registered, &lpTypes.Transcoder{Address: caller}) - client.On("TranscoderPool").Return(registered, nil) - - seed := big.NewInt(3) - ok, err = initializer.shouldInitialize(seed) - assert.Nil(err) - assert.False(ok) - - // Test caller selected - seed = big.NewInt(5) - ok, err = initializer.shouldInitialize(seed) - assert.Nil(err) - assert.True(ok) -} - func TestRoundInitializer_TryInitialize(t *testing.T) { client := &MockClient{} tw := &stubTimeWatcher{ @@ -101,45 +23,17 @@ func TestRoundInitializer_TryInitialize(t *testing.T) { lastInitializedRound: big.NewInt(100), lastInitializedBlockHash: [32]byte{123}, } - initializer := NewRoundInitializer(client, tw) + initializer := NewRoundInitializer(client, tw, 0) initializer.nextRoundStartL1Block = big.NewInt(5) assert := assert.New(t) - // Test error checking should initialize - expErr := errors.New("shouldInitialize error") - client.On("TranscoderPool").Return(nil, expErr).Once() - - err := initializer.tryInitialize() - assert.EqualError(err, expErr.Error()) - - // Test should not initialize - caller := ethcommon.BytesToAddress([]byte("foo")) - client.On("Account").Return(accounts.Account{Address: caller}) - - registered := []*lpTypes.Transcoder{ - {Address: ethcommon.BytesToAddress([]byte("jar"))}, - } - client.On("TranscoderPool").Return(registered, nil).Once() - - err = initializer.tryInitialize() - assert.Nil(err) - - // Test error when submitting initialization tx - registered = []*lpTypes.Transcoder{{Address: caller}} - client.On("TranscoderPool").Return(registered, nil) - expErr = errors.New("InitializeRound error") - client.On("InitializeRound").Return(nil, expErr).Once() - - err = initializer.tryInitialize() - assert.EqualError(err, expErr.Error()) - // Test error checking initialization tx tx := &types.Transaction{} client.On("InitializeRound").Return(tx, nil) - expErr = errors.New("CheckTx error") + expErr := errors.New("CheckTx error") client.On("CheckTx", mock.Anything).Return(expErr).Once() - err = initializer.tryInitialize() + err := initializer.tryInitialize() assert.EqualError(err, expErr.Error()) // Test success diff --git a/eth/watchers/pricefeedwatcher.go b/eth/watchers/pricefeedwatcher.go new file mode 100644 index 000000000..1bab9dc13 --- /dev/null +++ b/eth/watchers/pricefeedwatcher.go @@ -0,0 +1,234 @@ +package watchers + +import ( + "context" + "fmt" + "strings" + "sync" + "time" + + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/event" + "github.com/livepeer/go-livepeer/clog" + "github.com/livepeer/go-livepeer/eth" +) + +const ( + priceUpdateMaxRetries = 5 + priceUpdateBaseRetryDelay = 30 * time.Second + priceUpdatePeriod = 1 * time.Hour +) + +type PriceFeedWatcher interface { + Currencies() (base string, quote string, err error) + Current() (eth.PriceData, error) + Subscribe(ctx context.Context, sink chan<- eth.PriceData) +} + +// PriceFeedWatcher monitors a Chainlink PriceFeed for updated pricing info. It +// allows fetching the current price as well as listening for updates on the +// PriceUpdated channel. +type priceFeedWatcher struct { + baseRetryDelay time.Duration + + priceFeed eth.PriceFeedEthClient + + mu sync.RWMutex + current eth.PriceData + cancelWatch func() + + priceEventFeed event.Feed + subscriptions event.SubscriptionScope +} + +// NewPriceFeedWatcher creates a new PriceFeedWatcher instance. It will already +// fetch the current price and start a goroutine to watch for updates. +func NewPriceFeedWatcher(ethClient *ethclient.Client, priceFeedAddr string) (PriceFeedWatcher, error) { + priceFeed, err := eth.NewPriceFeedEthClient(ethClient, priceFeedAddr) + if err != nil { + return nil, fmt.Errorf("failed to create price feed client: %w", err) + } + w := &priceFeedWatcher{ + baseRetryDelay: priceUpdateBaseRetryDelay, + priceFeed: priceFeed, + } + return w, nil +} + +// Currencies returns the base and quote currencies of the price feed. +// i.e. base = CurrentPrice() * quote +func (w *priceFeedWatcher) Currencies() (base string, quote string, err error) { + description, err := w.priceFeed.Description() + if err != nil { + return "", "", fmt.Errorf("failed to get description: %w", err) + } + + base, quote, err = parseCurrencies(description) + if err != nil { + return "", "", err + } + return +} + +// Current returns the latest fetched price data, or fetches it in case it has +// not been fetched yet. +func (w *priceFeedWatcher) Current() (eth.PriceData, error) { + w.mu.RLock() + current := w.current + w.mu.RUnlock() + if current.UpdatedAt.IsZero() { + return w.updatePrice() + } + return current, nil +} + +// Subscribe allows one to subscribe to price updates emitted by the Watcher. +// The sink channel should have ample buffer space to avoid blocking other +// subscribers. Slow subscribers are not dropped. The subscription is kept alive +// until the passed Context is cancelled. +// +// The watch loop is run automatically while there are active subscriptions. It +// will be started when the first subscription is made and is automatically +// stopped when the last subscription is closed. +func (w *priceFeedWatcher) Subscribe(ctx context.Context, sink chan<- eth.PriceData) { + w.mu.Lock() + defer w.mu.Unlock() + w.ensureWatchLocked() + + sub := w.subscriptions.Track(w.priceEventFeed.Subscribe(sink)) + go w.handleUnsubscribe(ctx, sub) +} + +// updatePrice fetches the latest price data from the price feed and updates the +// current price if it is newer. If the price is updated, it will also send the +// updated price to the price event feed. +func (w *priceFeedWatcher) updatePrice() (eth.PriceData, error) { + newPrice, err := w.priceFeed.FetchPriceData() + if err != nil { + return eth.PriceData{}, fmt.Errorf("failed to fetch price data: %w", err) + } + + if newPrice.UpdatedAt.After(w.current.UpdatedAt) { + w.mu.Lock() + w.current = newPrice + w.mu.Unlock() + w.priceEventFeed.Send(newPrice) + } + + return newPrice, nil +} + +// ensureWatchLocked makes sure that the watch process is running. It assumes it +// is already running in a locked context (w.mu). The watch process itself will +// run in background and periodically poll the price feed for updates until the +// `w.cancelWatch` function is called. +func (w *priceFeedWatcher) ensureWatchLocked() { + if w.cancelWatch != nil { + // already running + return + } + ctx, cancel := context.WithCancel(context.Background()) + w.cancelWatch = cancel + + ticker := newTruncatedTicker(ctx, priceUpdatePeriod) + go w.watchTicker(ctx, ticker) +} + +// handleUnsubscribe waits for the provided Context to be done and then closes +// the given subscription. It then stops the watch process if there are no more +// active subscriptions. +func (w *priceFeedWatcher) handleUnsubscribe(ctx context.Context, sub event.Subscription) { +loop: + for { + select { + case <-ctx.Done(): + break loop + case <-sub.Err(): + clog.Errorf(ctx, "PriceFeedWatcher subscription error: %v", sub.Err()) + } + } + w.mu.Lock() + defer w.mu.Unlock() + + sub.Unsubscribe() + if w.subscriptions.Count() == 0 && w.cancelWatch != nil { + w.cancelWatch() + w.cancelWatch = nil + } +} + +// watchTicker is the main loop that periodically fetches the latest price data +// from the price feed. It's lifecycle is handled through the ensureWatch and +// handleUnsubscribe functions. +func (w *priceFeedWatcher) watchTicker(ctx context.Context, ticker <-chan time.Time) { + clog.V(6).Infof(ctx, "Starting PriceFeed watch loop") + for { + select { + case <-ctx.Done(): + clog.V(6).Infof(ctx, "Stopping PriceFeed watch loop") + return + case <-ticker: + attempt, retryDelay := 1, w.baseRetryDelay + for { + _, err := w.updatePrice() + if err == nil { + break + } else if attempt >= priceUpdateMaxRetries { + clog.Errorf(ctx, "Failed to fetch updated price from PriceFeed attempts=%d err=%q", attempt, err) + break + } + + clog.Warningf(ctx, "Failed to fetch updated price from PriceFeed, retrying after retryDelay=%d attempt=%d err=%q", retryDelay, attempt, err) + select { + case <-ctx.Done(): + return + case <-time.After(retryDelay): + } + attempt, retryDelay = attempt+1, retryDelay*2 + } + } + } +} + +// parseCurrencies parses the base and quote currencies from a price feed based +// on Chainlink PriceFeed description pattern "FROM / TO". +func parseCurrencies(description string) (currencyBase string, currencyQuote string, err error) { + currencies := strings.Split(description, "/") + if len(currencies) != 2 { + return "", "", fmt.Errorf("aggregator description must be in the format 'FROM / TO' but got: %s", description) + } + + currencyBase = strings.TrimSpace(currencies[0]) + currencyQuote = strings.TrimSpace(currencies[1]) + return +} + +// newTruncatedTicker creates a ticker that ticks at the next time that is a +// multiple of d, starting from the current time. This is a best-effort approach +// to ensure that nodes update their prices around the same time to avoid too +// big price discrepancies. +func newTruncatedTicker(ctx context.Context, d time.Duration) <-chan time.Time { + ch := make(chan time.Time, 1) + go func() { + // Do not close the channel, to prevent a concurrent goroutine reading from + // the channel from seeing an erroneous "tick" after its closed. + + nextTick := time.Now().UTC().Truncate(d) + for { + nextTick = nextTick.Add(d) + untilNextTick := nextTick.Sub(time.Now().UTC()) + if untilNextTick <= 0 { + continue + } + + select { + case <-ctx.Done(): + return + case t := <-time.After(untilNextTick): + ch <- t + } + } + }() + + return ch +} diff --git a/eth/watchers/pricefeedwatcher_test.go b/eth/watchers/pricefeedwatcher_test.go new file mode 100644 index 000000000..27eb5d24d --- /dev/null +++ b/eth/watchers/pricefeedwatcher_test.go @@ -0,0 +1,249 @@ +package watchers + +import ( + "context" + "errors" + "math/big" + "reflect" + "testing" + "time" + + "github.com/livepeer/go-livepeer/eth" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +type mockPriceFeedEthClient struct { + mock.Mock +} + +func (m *mockPriceFeedEthClient) FetchPriceData() (eth.PriceData, error) { + args := m.Called() + return args.Get(0).(eth.PriceData), args.Error(1) +} + +func (m *mockPriceFeedEthClient) Description() (string, error) { + args := m.Called() + return args.String(0), args.Error(1) +} + +func TestPriceFeedWatcher_UpdatePrice(t *testing.T) { + priceFeedMock := new(mockPriceFeedEthClient) + defer priceFeedMock.AssertExpectations(t) + + priceData := eth.PriceData{ + RoundID: 10, + Price: big.NewRat(3, 2), + UpdatedAt: time.Now(), + } + priceFeedMock.On("FetchPriceData").Return(priceData, nil).Once() + + w := &priceFeedWatcher{priceFeed: priceFeedMock} + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + priceUpdated := make(chan eth.PriceData, 1) + w.Subscribe(ctx, priceUpdated) + + newPrice, err := w.updatePrice() + require.NoError(t, err) + require.Equal(t, priceData, newPrice) + + select { + case updatedPrice := <-priceUpdated: + require.Equal(t, priceData, updatedPrice) + case <-time.After(2 * time.Second): + t.Error("Updated price hasn't been received on channel") + } +} + +func TestPriceFeedWatcher_Subscribe(t *testing.T) { + require := require.New(t) + priceFeedMock := new(mockPriceFeedEthClient) + defer priceFeedMock.AssertExpectations(t) + + w := &priceFeedWatcher{priceFeed: priceFeedMock} + + // Start a bunch of subscriptions and make sure only 1 watch loop gets started + observedCancelWatch := []context.CancelFunc{} + cancelSub := []context.CancelFunc{} + for i := 0; i < 5; i++ { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + w.Subscribe(ctx, make(chan eth.PriceData, 1)) + + observedCancelWatch = append(observedCancelWatch, w.cancelWatch) + cancelSub = append(cancelSub, cancel) + } + + require.NotNil(w.cancelWatch) + for i := range observedCancelWatch { + require.Equal(reflect.ValueOf(w.cancelWatch).Pointer(), reflect.ValueOf(observedCancelWatch[i]).Pointer()) + } + + // Stop all but the last subscription and ensure watch loop stays running + for i := 0; i < 4; i++ { + cancelSub[i]() + require.NotNil(w.cancelWatch) + } + + // Now stop the last subscription and ensure watch loop gets stopped + cancelSub[4]() + time.Sleep(1 * time.Second) + require.Nil(w.cancelWatch) + + // Finally, just make sure it can be started again after having been stopped + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + w.Subscribe(ctx, make(chan eth.PriceData, 1)) + require.NotNil(w.cancelWatch) +} + +func TestPriceFeedWatcher_Watch(t *testing.T) { + require := require.New(t) + priceFeedMock := new(mockPriceFeedEthClient) + defer priceFeedMock.AssertExpectations(t) + + w := &priceFeedWatcher{priceFeed: priceFeedMock} + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + priceUpdated := make(chan eth.PriceData, 1) + w.Subscribe(ctx, priceUpdated) + + priceData := eth.PriceData{ + RoundID: 10, + Price: big.NewRat(9, 2), + UpdatedAt: time.Now(), + } + checkPriceUpdated := func() { + select { + case updatedPrice := <-priceUpdated: + require.Equal(priceData, updatedPrice) + require.Equal(priceData, w.current) + case <-time.After(1 * time.Second): + require.Fail("Updated price hasn't been received on channel in a timely manner") + } + priceFeedMock.AssertExpectations(t) + } + checkNoPriceUpdate := func() { + select { + case <-priceUpdated: + require.Fail("Unexpected price update given it hasn't changed") + case <-time.After(1 * time.Second): + // all good + } + priceFeedMock.AssertExpectations(t) + } + + // Start the watch loop + fakeTicker := make(chan time.Time, 10) + go func() { + w.watchTicker(ctx, fakeTicker) + }() + + // First time should trigger an update + priceFeedMock.On("FetchPriceData").Return(priceData, nil).Once() + fakeTicker <- time.Now() + checkPriceUpdated() + + // Trigger a dummy update given price hasn't changed + priceFeedMock.On("FetchPriceData").Return(priceData, nil).Once() + fakeTicker <- time.Now() + checkNoPriceUpdate() + + // still shouldn't update given UpdatedAt stayed the same + priceData.Price = big.NewRat(1, 1) + priceFeedMock.On("FetchPriceData").Return(priceData, nil).Once() + fakeTicker <- time.Now() + checkNoPriceUpdate() + + // bump the UpdatedAt time to trigger an update + priceData.UpdatedAt = priceData.UpdatedAt.Add(1 * time.Minute) + priceFeedMock.On("FetchPriceData").Return(priceData, nil).Once() + fakeTicker <- time.Now() + checkPriceUpdated() + + priceData.UpdatedAt = priceData.UpdatedAt.Add(1 * time.Hour) + priceData.Price = big.NewRat(3, 2) + priceFeedMock.On("FetchPriceData").Return(priceData, nil).Once() + fakeTicker <- time.Now() + checkPriceUpdated() +} + +func TestPriceFeedWatcher_WatchErrorRetries(t *testing.T) { + priceFeedMock := new(mockPriceFeedEthClient) + defer priceFeedMock.AssertExpectations(t) + + // First 4 calls should fail then succeed on the 5th + for i := 0; i < 4; i++ { + priceFeedMock.On("FetchPriceData").Return(eth.PriceData{}, errors.New("error")).Once() + } + priceData := eth.PriceData{ + RoundID: 10, + Price: big.NewRat(3, 2), + UpdatedAt: time.Now(), + } + priceFeedMock.On("FetchPriceData").Return(priceData, nil) + + w := &priceFeedWatcher{ + baseRetryDelay: 5 * time.Millisecond, + priceFeed: priceFeedMock, + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + priceUpdated := make(chan eth.PriceData, 1) + w.Subscribe(ctx, priceUpdated) + + // Start watch loop + fakeTicker := make(chan time.Time, 10) + go func() { + w.watchTicker(ctx, fakeTicker) + }() + + fakeTicker <- time.Now() + select { + case updatedPrice := <-priceUpdated: + require.Equal(t, priceData, updatedPrice) + case <-time.After(2 * time.Second): + t.Error("Updated price hasn't been received on channel") + } +} + +func TestParseCurrencies(t *testing.T) { + t.Run("Valid currencies", func(t *testing.T) { + description := "ETH / USD" + currencyBase, currencyQuote, err := parseCurrencies(description) + + require.NoError(t, err) + require.Equal(t, "ETH", currencyBase) + require.Equal(t, "USD", currencyQuote) + }) + + t.Run("Missing separator", func(t *testing.T) { + description := "ETHUSD" + _, _, err := parseCurrencies(description) + + require.Error(t, err) + require.Contains(t, err.Error(), "aggregator description must be in the format 'FROM / TO'") + }) + + t.Run("Extra spaces", func(t *testing.T) { + description := " ETH / USD " + currencyBase, currencyQuote, err := parseCurrencies(description) + + require.NoError(t, err) + require.Equal(t, "ETH", currencyBase) + require.Equal(t, "USD", currencyQuote) + }) + + t.Run("Lowercase currency", func(t *testing.T) { + description := "eth / usd" + currencyBase, currencyQuote, err := parseCurrencies(description) + + require.NoError(t, err) + require.Equal(t, "eth", currencyBase) + require.Equal(t, "usd", currencyQuote) + }) +} diff --git a/go.mod b/go.mod index c80fe64f3..1a59fc36f 100644 --- a/go.mod +++ b/go.mod @@ -4,10 +4,11 @@ go 1.21.5 require ( contrib.go.opencensus.io/exporter/prometheus v0.4.2 + github.com/Masterminds/semver/v3 v3.2.1 github.com/cenkalti/backoff v2.2.1+incompatible github.com/ethereum/go-ethereum v1.13.5 github.com/getkin/kin-openapi v0.124.0 - github.com/golang/glog v1.1.1 + github.com/golang/glog v1.2.1 github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.4 github.com/jaypipes/ghw v0.10.0 @@ -15,7 +16,7 @@ require ( github.com/livepeer/ai-worker v0.1.0 github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 - github.com/livepeer/lpms v0.0.0-20240711175220-227325841434 + github.com/livepeer/lpms v0.0.0-20240731140137-28406cf8bc78 github.com/livepeer/m3u8 v0.11.1 github.com/mattn/go-sqlite3 v1.14.18 github.com/oapi-codegen/nethttp-middleware v1.0.1 @@ -31,27 +32,34 @@ require ( go.opencensus.io v0.24.0 go.uber.org/goleak v1.3.0 golang.org/x/net v0.25.0 - google.golang.org/grpc v1.57.1 - google.golang.org/protobuf v1.33.0 + google.golang.org/grpc v1.65.0 + google.golang.org/protobuf v1.34.1 pgregory.net/rapid v1.1.0 ) require ( cloud.google.com/go v0.110.2 // indirect - cloud.google.com/go/compute v1.20.0 // indirect - cloud.google.com/go/compute/metadata v0.2.3 // indirect + cloud.google.com/go/compute/metadata v0.3.0 // indirect cloud.google.com/go/iam v1.1.0 // indirect cloud.google.com/go/storage v1.30.1 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/DataDog/zstd v1.4.5 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/StackExchange/wmi v1.2.1 // indirect + github.com/VictoriaMetrics/fastcache v1.12.1 // indirect github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/aws/aws-sdk-go v1.44.273 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.7.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect github.com/cespare/cp v1.1.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cockroachdb/errors v1.8.1 // indirect + github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f // indirect + github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 // indirect + github.com/cockroachdb/redact v1.0.8 // indirect + github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect github.com/consensys/bavard v0.1.13 // indirect github.com/consensys/gnark-crypto v0.12.1 // indirect github.com/containerd/containerd v1.7.0-beta.2 // indirect @@ -60,6 +68,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/deckarep/golang-set/v2 v2.1.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect + github.com/deepmap/oapi-codegen v1.6.0 // indirect github.com/deepmap/oapi-codegen/v2 v2.2.0 // indirect github.com/distribution/reference v0.5.0 // indirect github.com/dlclark/regexp2 v1.7.0 // indirect @@ -72,6 +81,7 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/ethereum/c-kzg-4844 v0.4.0 // indirect github.com/fatih/color v1.13.0 // indirect + github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect github.com/ghodss/yaml v1.0.0 // indirect @@ -86,19 +96,28 @@ require ( github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/go-test/deep v1.1.0 // indirect + github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect - github.com/google/go-cmp v0.6.0 // indirect github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect github.com/google/s2a-go v0.1.4 // indirect - github.com/google/uuid v1.5.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect github.com/googleapis/gax-go/v2 v2.10.0 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/websocket v1.4.2 // indirect + github.com/graph-gophers/graphql-go v1.3.0 // indirect + github.com/hashicorp/go-bexpr v0.1.10 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 // indirect + github.com/holiman/bloomfilter/v2 v2.0.3 // indirect github.com/holiman/uint256 v1.2.3 // indirect + github.com/huin/goupnp v1.3.0 // indirect + github.com/influxdata/influxdb-client-go/v2 v2.4.0 // indirect + github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c // indirect + github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect github.com/invopop/yaml v0.2.0 // indirect github.com/ipfs/bbloom v0.0.4 // indirect github.com/ipfs/go-block-format v0.1.2 // indirect @@ -122,6 +141,7 @@ require ( github.com/ipld/go-car v0.6.0 // indirect github.com/ipld/go-codec-dagpb v1.6.0 // indirect github.com/ipld/go-ipld-prime v0.20.0 // indirect + github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/goprocess v0.1.4 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -129,6 +149,8 @@ require ( github.com/karalabe/usb v0.0.2 // indirect github.com/klauspost/compress v1.17.4 // indirect github.com/klauspost/cpuid/v2 v2.2.6 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -139,6 +161,8 @@ require ( github.com/minio/minio-go/v7 v7.0.66 // indirect github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/mapstructure v1.4.1 // indirect + github.com/mitchellh/pointerstructure v1.2.0 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/sys/sequential v0.5.0 // indirect @@ -167,6 +191,8 @@ require ( github.com/rabbitmq/amqp091-go v1.8.0 // indirect github.com/rabbitmq/rabbitmq-stream-go-client v1.1.1 // indirect github.com/rivo/uniseg v0.2.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/rs/cors v1.7.0 // indirect github.com/rs/xid v1.5.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect @@ -175,10 +201,14 @@ require ( github.com/status-im/keycard-go v0.2.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/supranational/blst v0.3.11 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect + github.com/tyler-smith/go-bip39 v1.1.0 // indirect + github.com/urfave/cli/v2 v2.25.7 // indirect github.com/vincent-petithory/dataurl v1.0.0 // indirect github.com/whyrusleeping/cbor-gen v0.0.0-20230418232409-daab9ece03a0 // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect go.opentelemetry.io/otel v1.16.0 // indirect go.opentelemetry.io/otel/metric v1.16.0 // indirect go.opentelemetry.io/otel/trace v1.16.0 // indirect @@ -188,18 +218,20 @@ require ( golang.org/x/crypto v0.23.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/oauth2 v0.8.0 // indirect + golang.org/x/oauth2 v0.20.0 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.20.0 // indirect golang.org/x/text v0.15.0 // indirect + golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.21.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.125.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect howett.net/plist v1.0.0 // indirect diff --git a/go.sum b/go.sum index 74df1b726..2e9eacc71 100644 --- a/go.sum +++ b/go.sum @@ -21,10 +21,8 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.20.0 h1:cUOcywWuowO9It2i1KX1lIb0HH7gLv6nENKuZGnlcSo= -cloud.google.com/go/compute v1.20.0/go.mod h1:kn5BhC++qUWR/AM3Dn21myV7QbgqejW04cAOrtppaQI= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= +cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/iam v1.1.0 h1:67gSqaPukx7O8WLLHMa0PNs3EBGd2eE4d+psbO/CO94= @@ -45,14 +43,23 @@ contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20221206110420-d395f97c4830 h1:u8scGKApGy+gXpYDw2f+nh60R0FqCfrpDRIQki+5o3U= github.com/AdaLogics/go-fuzz-headers v0.0.0-20221206110420-d395f97c4830/go.mod h1:VzwV+t+dZ9j/H867F1M2ziD+yLHtB46oM35FxxMJ4d0= +github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw= +github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= +github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM= +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= @@ -60,21 +67,27 @@ github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXn github.com/Microsoft/hcsshim v0.10.0-rc.1 h1:Lms8jwpaIdIUvoBNee8ZuvIi1XnNy9uvnxSC9L1q1x4= github.com/Microsoft/hcsshim v0.10.0-rc.1/go.mod h1:7XX96hdvnwWGdXnksDNdhfFcUH1BtQY6bL2L3f9Abyk= github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= +github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= +github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/aws/aws-sdk-go v1.44.273 h1:CX8O0gK+cGrgUyv7bgJ6QQP9mQg7u5mweHdNzULH47c= github.com/aws/aws-sdk-go v1.44.273/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -96,8 +109,9 @@ github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= @@ -113,6 +127,10 @@ github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XP github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/errors v1.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM= github.com/cockroachdb/errors v1.8.1 h1:A5+txlVZfOqFBDa4mGz2bUWSp0aHElvHX2bKkdbQu+Y= github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac= github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY= @@ -125,6 +143,7 @@ github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 h1:IKgmqgMQlVJIZj19CdocBeS github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= @@ -134,7 +153,11 @@ github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMX github.com/containerd/containerd v1.7.0-beta.2 h1:GWmC96y8j7jlFJX0Wh+covft0M1hHBqQL7lo+N6qvxg= github.com/containerd/containerd v1.7.0-beta.2/go.mod h1:RR01Jsm/jovDKK48sFCVqWyKAH2APMPi88Aeu1on63I= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -143,6 +166,7 @@ github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXk github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0= github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis= +github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -154,8 +178,13 @@ github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= +github.com/deepmap/oapi-codegen v1.6.0 h1:w/d1ntwh91XI0b/8ja7+u5SvA4IFfM0UNNLmiDR1gg0= +github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= github.com/deepmap/oapi-codegen/v2 v2.2.0 h1:FW4f7C0Xb6EaezBSB3GYw2QGwHD5ChDflG+3xSZBdvY= github.com/deepmap/oapi-codegen/v2 v2.2.0/go.mod h1:L4zUv7ULYDtYSb/aYk/xO3OYcQU6BoU/0viULkbi2DE= +github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= @@ -180,38 +209,53 @@ github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127 h1:qwcF+vdFrvPSEUDSX5R github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= github.com/ethereum/go-ethereum v1.13.5 h1:U6TCRciCqZRe4FPXmy1sMGxTfuk8P7u2UoinF3VbaFk= github.com/ethereum/go-ethereum v1.13.5/go.mod h1:yMTu38GSuyxaYzQMViqNmQ1s3cE84abZexQmTgenWk0= +github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= +github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= 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/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/getkin/kin-openapi v0.124.0 h1:VSFNMB9C9rTKBnQ/fpyDU8ytMTr4dWI9QovSKj9kz/M= github.com/getkin/kin-openapi v0.124.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= +github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s= github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= 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= @@ -231,11 +275,14 @@ github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicbw= github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= @@ -254,19 +301,25 @@ github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.1.1 h1:jxpi2eWoU84wbX9iIEyAeeoac3FLuifZpY9tcNUD9kw= -github.com/golang/glog v1.1.1/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= +github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= +github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -299,8 +352,11 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= +github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -316,6 +372,7 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= @@ -338,8 +395,8 @@ github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= -github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -350,19 +407,25 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGa github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0= +github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw= github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= @@ -372,8 +435,17 @@ github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZm github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= +github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb-client-go/v2 v2.4.0 h1:HGBfZYStlx3Kqvsv1h2pJixbCl/jhnFtxpKFAv9Tu5k= +github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8= +github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c h1:qSHzRbhzK8RdXOsAdfDgO49TtqC1oZ+acxPrkfTxcCs= +github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU= +github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= @@ -450,6 +522,10 @@ github.com/ipld/go-codec-dagpb v1.6.0/go.mod h1:ANzFhfP2uMJxRBr8CE+WQWs5UsNa0pYt github.com/ipld/go-ipld-prime v0.9.1-0.20210324083106-dc342a9917db/go.mod h1:KvBLMr4PX1gWptgkzRjVZCrLmSGcZCb/jioOQwCqZN8= github.com/ipld/go-ipld-prime v0.20.0 h1:Ud3VwE9ClxpO2LkCYP7vWPc0Fo+dYdYzgxUJZ3uRG4g= github.com/ipld/go-ipld-prime v0.20.0/go.mod h1:PzqZ/ZR981eKbgdr3y2DJYeD/8bgMawdGVlJDE8kK+M= +github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= +github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= +github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI= +github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jaypipes/ghw v0.10.0 h1:UHu9UX08Py315iPojADFPOkmjTsNzHj4g4adsNKKteY= @@ -478,15 +554,28 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= +github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= +github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/karalabe/usb v0.0.2 h1:M6QQBNxF+CQ8OFvxrT90BA0qBOXymndZnk5q235mFc4= github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= +github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk= +github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U= +github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw= +github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= @@ -507,6 +596,9 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= +github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= +github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= @@ -539,15 +631,25 @@ github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cO github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded/go.mod h1:xkDdm+akniYxVT9KW1Y2Y7Hso6aW+rZObz3nrA9yTHw= github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 h1:4oH3NqV0NvcdS44Ld3zK2tO8IUiNozIggm74yobQeZg= github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18/go.mod h1:Jpf4jHK+fbWioBHRDRM1WadNT1qmY27g2YicTdO0Rtc= -github.com/livepeer/lpms v0.0.0-20240711175220-227325841434 h1:E7PKN6q/jMLapEV+eEwlwv87Xe5zacaVhvZ8T6AJR3c= -github.com/livepeer/lpms v0.0.0-20240711175220-227325841434/go.mod h1:Hr/JhxxPDipOVd4ZrGYWrdJfpVF8/SEI0nNr2ctAlkM= +github.com/livepeer/lpms v0.0.0-20240731140137-28406cf8bc78 h1:J026gPdu533qY+KNEgWaSiu3dExW+DB4UyE8+PAEKvg= +github.com/livepeer/lpms v0.0.0-20240731140137-28406cf8bc78/go.mod h1:z5ROP1l5OzAKSoqVRLc34MjUdueil6wHSecQYV7llIw= github.com/livepeer/m3u8 v0.11.1 h1:VkUJzfNTyjy9mqsgp5JPvouwna8wGZMvd/gAfT5FinU= github.com/livepeer/m3u8 v0.11.1/go.mod h1:IUqAtwWPAG2CblfQa4SVzTQoDcEMPyfNOaBSxqHMS04= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -561,9 +663,13 @@ github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.18 h1:JL0eqdCOq6DJVNPSvArO/bIV9/P7fbGrV00LZHc+5aI= github.com/mattn/go-sqlite3 v1.14.18/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg= +github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ= +github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= @@ -578,6 +684,7 @@ github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dz github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= @@ -604,6 +711,7 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwd github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= @@ -643,6 +751,11 @@ github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/n github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM= +github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oapi-codegen/nethttp-middleware v1.0.1 h1:ZWvwfnMU0eloHX1VEJmQscQm3741t0vCm0eSIie1NIo= github.com/oapi-codegen/nethttp-middleware v1.0.1/go.mod h1:P7xtAvpoqNB+5obR9qRCeefH7YlXWSK3KgPs/9WB8tE= github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= @@ -650,11 +763,16 @@ github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= +github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs= github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.22.1 h1:pY8O4lBfsHKZHM/6nrxkhVPUznOlIu3quZcKP/M20KI= github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -668,10 +786,12 @@ github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/ github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/peterbourgon/ff/v3 v3.4.0 h1:QBvM/rizZM1cB0p0lGMdmR7HxZeI/ZrBWB4DqLkMUBc= @@ -680,6 +800,9 @@ github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQm github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -731,16 +854,21 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= @@ -761,7 +889,12 @@ github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+ github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= @@ -796,8 +929,10 @@ github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+F github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= @@ -807,6 +942,12 @@ github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8= github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/vincent-petithory/dataurl v1.0.0 h1:cXw+kPto8NLuJtlMsI152irrVw9fRDX8AbShPRpg2CI= github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= @@ -820,8 +961,16 @@ github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvS github.com/whyrusleeping/cbor-gen v0.0.0-20200123233031-1cdf64d27158/go.mod h1:Xj/M2wWU+QdTdRbu/L/1dIZY8/Wb2K9pAhtroQuxJJI= github.com/whyrusleeping/cbor-gen v0.0.0-20230418232409-daab9ece03a0 h1:XYEgH2nJgsrcrj32p+SAbx6T3s/6QknOXezXtz7kzbg= github.com/whyrusleeping/cbor-gen v0.0.0-20230418232409-daab9ece03a0/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= +github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= +github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -861,13 +1010,17 @@ go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= @@ -913,9 +1066,11 @@ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -924,6 +1079,7 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -934,13 +1090,16 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -958,8 +1117,8 @@ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= +golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -979,8 +1138,10 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -989,11 +1150,16 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1009,10 +1175,13 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1052,6 +1221,7 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= @@ -1061,16 +1231,21 @@ golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180810170437-e96c4e24768d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -1147,6 +1322,7 @@ google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCID google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1179,10 +1355,11 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao= google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= -google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM= -google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1200,8 +1377,8 @@ google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTp google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.57.1 h1:upNTNqv0ES+2ZOOqACwVtS3Il8M12/+Hz41RCPzAjQg= -google.golang.org/grpc v1.57.1/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1217,8 +1394,9 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1227,10 +1405,14 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/install_ffmpeg.sh b/install_ffmpeg.sh index df864b496..3de15fdc1 100755 --- a/install_ffmpeg.sh +++ b/install_ffmpeg.sh @@ -1,230 +1,3 @@ #!/usr/bin/env bash - -set -exuo pipefail - -ROOT="${1:-$HOME}" -NPROC=${NPROC:-$(nproc)} -EXTRA_CFLAGS="" -EXTRA_LDFLAGS="" -EXTRA_X264_FLAGS="" -EXTRA_FFMPEG_FLAGS="" -BUILD_TAGS="${BUILD_TAGS:-}" - -# Build platform flags -BUILDOS=$(uname -s | tr '[:upper:]' '[:lower:]') -BUILDARCH=$(uname -m | tr '[:upper:]' '[:lower:]') -if [[ $BUILDARCH == "aarch64" ]]; then - BUILDARCH=arm64 -fi -if [[ $BUILDARCH == "x86_64" ]]; then - BUILDARCH=amd64 -fi - -# Override these for cross-compilation -export GOOS="${GOOS:-$BUILDOS}" -export GOARCH="${GOARCH:-$BUILDARCH}" - -echo "BUILDOS: $BUILDOS" -echo "BUILDARCH: $BUILDARCH" -echo "GOOS: $GOOS" -echo "GOARCH: $GOARCH" - -function check_sysroot() { - if ! stat $SYSROOT > /dev/null; then - echo "cross-compilation sysroot not found at $SYSROOT, try setting SYSROOT to the correct path" - exit 1 - fi -} - -if [[ "$BUILDARCH" == "amd64" && "$BUILDOS" == "linux" && "$GOARCH" == "arm64" && "$GOOS" == "linux" ]]; then - echo "cross-compiling linux-amd64 --> linux-arm64" - export CC="clang-14" - export STRIP="llvm-strip-14" - export AR="llvm-ar-14" - export RANLIB="llvm-ranlib-14" - EXTRA_CFLAGS="--target=aarch64-linux-gnu -I/usr/local/cuda_arm64/include $EXTRA_CFLAGS" - EXTRA_LDFLAGS="-fuse-ld=lld --target=aarch64-linux-gnu -L/usr/local/cuda_arm64/lib64 $EXTRA_LDFLAGS" - EXTRA_FFMPEG_FLAGS="$EXTRA_FFMPEG_FLAGS --arch=aarch64 --enable-cross-compile --cc=clang --strip=llvm-strip-14" - HOST_OS="--host=aarch64-linux-gnu" -fi - -if [[ "$BUILDARCH" == "arm64" && "$BUILDOS" == "darwin" && "$GOARCH" == "arm64" && "$GOOS" == "linux" ]]; then - SYSROOT="${SYSROOT:-"/tmp/sysroot-aarch64-linux-gnu"}" - check_sysroot - echo "cross-compiling darwin-arm64 --> linux-arm64" - LLVM_PATH="${LLVM_PATH:-/opt/homebrew/opt/llvm/bin}" - if [[ ! -f "$LLVM_PATH/ld.lld" ]]; then - echo "llvm linker not found at '$LLVM_PATH/ld.lld'. try 'brew install llvm' or set LLVM_PATH to your LLVM bin directory" - exit 1 - fi - export CC="$LLVM_PATH/clang --sysroot=$SYSROOT" - export AR="/opt/homebrew/opt/llvm/bin/llvm-ar" - export RANLIB="/opt/homebrew/opt/llvm/bin/llvm-ranlib" - EXTRA_CFLAGS="--target=aarch64-linux-gnu $EXTRA_CFLAGS" - EXTRA_LDFLAGS="--target=aarch64-linux-gnu -fuse-ld=$LLVM_PATH/ld.lld $EXTRA_LDFLAGS" - EXTRA_FFMPEG_FLAGS="$EXTRA_FFMPEG_FLAGS --arch=aarch64 --enable-cross-compile --cc=$LLVM_PATH/clang --sysroot=$SYSROOT --ar=$AR --ranlib=$RANLIB --target-os=linux" - EXTRA_X264_FLAGS="$EXTRA_X264_FLAGS --sysroot=$SYSROOT --ar=$AR --ranlib=$RANLIB" - HOST_OS="--host=aarch64-linux-gnu" -fi - -if [[ "$BUILDOS" == "linux" && "$GOARCH" == "amd64" && "$GOOS" == "windows" ]]; then - echo "cross-compiling linux-$BUILDARCH --> windows-amd64" - SYSROOT="${SYSROOT:-"/usr/x86_64-w64-mingw32"}" - check_sysroot - EXTRA_CFLAGS="-L$SYSROOT/lib -I$SYSROOT/include $EXTRA_CFLAGS" - EXTRA_LDFLAGS="-L$SYSROOT/lib $EXTRA_LDFLAGS" - EXTRA_FFMPEG_FLAGS="$EXTRA_FFMPEG_FLAGS --arch=x86_64 --enable-cross-compile --cross-prefix=x86_64-w64-mingw32- --target-os=mingw64 --sysroot=$SYSROOT" - EXTRA_X264_FLAGS="$EXTRA_X264_FLAGS --cross-prefix=x86_64-w64-mingw32- --sysroot=$SYSROOT" - HOST_OS="--host=mingw64" - # Workaround for https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=967969 - export PKG_CONFIG_LIBDIR="/usr/local/x86_64-w64-mingw32/lib/pkgconfig" - EXTRA_FFMPEG_FLAGS="$EXTRA_FFMPEG_FLAGS --pkg-config=$(which pkg-config)" -fi - -if [[ "$BUILDARCH" == "amd64" && "$BUILDOS" == "darwin" && "$GOARCH" == "arm64" && "$GOOS" == "darwin" ]]; then - echo "cross-compiling darwin-amd64 --> darwin-arm64" - EXTRA_CFLAGS="$EXTRA_CFLAGS --target=arm64-apple-macos11" - EXTRA_LDFLAGS="$EXTRA_LDFLAGS --target=arm64-apple-macos11" - HOST_OS="--host=aarch64-darwin" - EXTRA_FFMPEG_FLAGS="$EXTRA_FFMPEG_FLAGS --arch=aarch64 --enable-cross-compile" -fi - -# Windows (MSYS2) needs a few tweaks -if [[ "$BUILDOS" == *"MSYS"* ]]; then - ROOT="/build" - export PATH="$PATH:/usr/bin:/mingw64/bin" - export C_INCLUDE_PATH="${C_INCLUDE_PATH:-}:/mingw64/lib" - - export PATH="$ROOT/compiled/bin":$PATH - export PKG_CONFIG_PATH=/mingw64/lib/pkgconfig - - export TARGET_OS="--target-os=mingw64" - export HOST_OS="--host=x86_64-w64-mingw32" - export BUILD_OS="--build=x86_64-w64-mingw32 --host=x86_64-w64-mingw32 --target=x86_64-w64-mingw32" - - # Needed for mbedtls - export WINDOWS_BUILD=1 -fi - -export PATH="$ROOT/compiled/bin:${PATH}" -export PKG_CONFIG_PATH="${PKG_CONFIG_PATH:-}:$ROOT/compiled/lib/pkgconfig" - -mkdir -p "$ROOT/" - -# NVENC only works on Windows/Linux -if [[ "$GOOS" != "darwin" ]]; then - if [[ ! -e "$ROOT/nv-codec-headers" ]]; then - git clone https://git.videolan.org/git/ffmpeg/nv-codec-headers.git "$ROOT/nv-codec-headers" - cd $ROOT/nv-codec-headers - git checkout n12.1.14.0 - make -e PREFIX="$ROOT/compiled" - make install -e PREFIX="$ROOT/compiled" - fi -fi - -if [[ "$GOOS" != "windows" && "$GOARCH" == "amd64" ]]; then - if [[ ! -e "$ROOT/nasm-2.14.02" ]]; then - # sudo apt-get -y install asciidoc xmlto # this fails :( - cd "$ROOT" - curl -o nasm-2.14.02.tar.gz https://www.nasm.us/pub/nasm/releasebuilds/2.14.02/nasm-2.14.02.tar.gz - echo 'b34bae344a3f2ed93b2ca7bf25f1ed3fb12da89eeda6096e3551fd66adeae9fc nasm-2.14.02.tar.gz' >nasm-2.14.02.tar.gz.sha256 - sha256sum -c nasm-2.14.02.tar.gz.sha256 - tar xf nasm-2.14.02.tar.gz - rm nasm-2.14.02.tar.gz nasm-2.14.02.tar.gz.sha256 - cd "$ROOT/nasm-2.14.02" - ./configure --prefix="$ROOT/compiled" - make -j$NPROC - make -j$NPROC install || echo "Installing docs fails but should be OK otherwise" - fi -fi - -if [[ ! -e "$ROOT/x264" ]]; then - git clone http://git.videolan.org/git/x264.git "$ROOT/x264" - cd "$ROOT/x264" - if [[ $GOARCH == "arm64" ]]; then - # newer git master, compiles on Apple Silicon - git checkout 66a5bc1bd1563d8227d5d18440b525a09bcf17ca - else - # older git master, does not compile on Apple Silicon - git checkout 545de2ffec6ae9a80738de1b2c8cf820249a2530 - fi - ./configure --prefix="$ROOT/compiled" --enable-pic --enable-static ${HOST_OS:-} --disable-cli --extra-cflags="$EXTRA_CFLAGS" --extra-asflags="$EXTRA_CFLAGS" --extra-ldflags="$EXTRA_LDFLAGS" $EXTRA_X264_FLAGS || (cat $ROOT/x264/config.log && exit 1) - make -j$NPROC - make -j$NPROC install-lib-static -fi - -if [[ "$GOOS" == "linux" && "$BUILD_TAGS" == *"debug-video"* ]]; then - sudo apt-get install -y libnuma-dev cmake - if [[ ! -e "$ROOT/x265" ]]; then - git clone https://bitbucket.org/multicoreware/x265_git.git "$ROOT/x265" - cd "$ROOT/x265" - git checkout 17839cc0dc5a389e27810944ae2128a65ac39318 - cd build/linux/ - cmake -DCMAKE_INSTALL_PREFIX=$ROOT/compiled -G "Unix Makefiles" ../../source - make -j$NPROC - make -j$NPROC install - fi - # VP8/9 support - if [[ ! -e "$ROOT/libvpx" ]]; then - git clone https://chromium.googlesource.com/webm/libvpx.git "$ROOT/libvpx" - cd "$ROOT/libvpx" - git checkout ab35ee100a38347433af24df05a5e1578172a2ae - ./configure --prefix="$ROOT/compiled" --disable-examples --disable-unit-tests --enable-vp9-highbitdepth --enable-shared --as=nasm - make -j$NPROC - make -j$NPROC install - fi -fi - -DISABLE_FFMPEG_COMPONENTS="" -EXTRA_FFMPEG_LDFLAGS="$EXTRA_LDFLAGS" -# all flags which should be present for production build, but should be replaced/removed for debug build -DEV_FFMPEG_FLAGS="" - -if [[ "$BUILDOS" == "darwin" && "$GOOS" == "darwin" ]]; then - EXTRA_FFMPEG_LDFLAGS="$EXTRA_FFMPEG_LDFLAGS -framework CoreFoundation -framework Security" -elif [[ "$GOOS" == "windows" ]]; then - EXTRA_FFMPEG_FLAGS="$EXTRA_FFMPEG_FLAGS --enable-cuda --enable-cuda-llvm --enable-cuvid --enable-nvenc --enable-decoder=h264_cuvid,hevc_cuvid,vp8_cuvid,vp9_cuvid --enable-filter=scale_cuda,signature_cuda,hwupload_cuda --enable-encoder=h264_nvenc,hevc_nvenc" -elif [[ -e "/usr/local/cuda/lib64" ]]; then - echo "CUDA SDK detected, building with GPU support" - EXTRA_FFMPEG_FLAGS="$EXTRA_FFMPEG_FLAGS --enable-nonfree --enable-cuda-nvcc --enable-libnpp --enable-cuda --enable-cuda-llvm --enable-cuvid --enable-nvenc --enable-decoder=h264_cuvid,hevc_cuvid,vp8_cuvid,vp9_cuvid --enable-filter=scale_npp,signature_cuda,hwupload_cuda --enable-encoder=h264_nvenc,hevc_nvenc" -else - echo "No CUDA SDK detected, building without GPU support" -fi - -if [[ $BUILD_TAGS == *"debug-video"* ]]; then - echo "video debug mode, building ffmpeg with tools, debug info and additional capabilities for running tests" - DEV_FFMPEG_FLAGS="--enable-muxer=md5,flv --enable-demuxer=hls --enable-filter=ssim,tinterlace --enable-encoder=wrapped_avframe,pcm_s16le " - DEV_FFMPEG_FLAGS+="--enable-shared --enable-debug=3 --disable-stripping --disable-optimizations --enable-encoder=libx265,libvpx_vp8,libvpx_vp9 " - DEV_FFMPEG_FLAGS+="--enable-decoder=hevc,libvpx_vp8,libvpx_vp9 --enable-libx265 --enable-libvpx --enable-bsf=noise " -else - # disable all unnecessary features for production build - DISABLE_FFMPEG_COMPONENTS+=" --disable-doc --disable-sdl2 --disable-iconv --disable-muxers --disable-demuxers --disable-parsers --disable-protocols " - DISABLE_FFMPEG_COMPONENTS+=" --disable-encoders --disable-decoders --disable-filters --disable-bsfs --disable-postproc --disable-lzma " -fi - -if [[ ! -e "$ROOT/ffmpeg/libavcodec/libavcodec.a" ]]; then - git clone https://github.com/livepeer/FFmpeg-6.1.1.git "$ROOT/ffmpeg" || echo "FFmpeg dir already exists" - cd "$ROOT/ffmpeg" - ./configure ${TARGET_OS:-} $DISABLE_FFMPEG_COMPONENTS --fatal-warnings \ - --enable-libx264 --enable-gpl \ - --enable-protocol=rtmp,file,pipe \ - --enable-muxer=mp3,wav,flac,mpegts,hls,segment,mp4,hevc,matroska,webm,null --enable-demuxer=mp3,wav,flac,flv,mpegts,mp4,mov,webm,matroska,image2 \ - --enable-bsf=h264_mp4toannexb,aac_adtstoasc,h264_metadata,h264_redundant_pps,hevc_mp4toannexb,extract_extradata \ - --enable-parser=mpegaudio,vorbis,opus,flac,aac,aac_latm,h264,hevc,vp8,vp9,png \ - --enable-filter=abuffer,buffer,abuffersink,buffersink,afifo,fifo,aformat,format \ - --enable-filter=aresample,asetnsamples,fps,scale,hwdownload,select,livepeer_dnn,signature \ - --enable-encoder=mp3,vorbis,flac,aac,opus,libx264 \ - --enable-decoder=mp3,vorbis,flac,aac,opus,h264,png \ - --extra-cflags="${EXTRA_CFLAGS} -I${ROOT}/compiled/include -I/usr/local/cuda/include" \ - --extra-ldflags="${EXTRA_FFMPEG_LDFLAGS} -L${ROOT}/compiled/lib -L/usr/local/cuda/lib64" \ - --prefix="$ROOT/compiled" \ - $EXTRA_FFMPEG_FLAGS \ - $DEV_FFMPEG_FLAGS || (tail -100 ${ROOT}/ffmpeg/ffbuild/config.log && exit 1) - # If configure fails, then print the last 100 log lines for debugging and exit. -fi - -if [[ ! -e "$ROOT/ffmpeg/libavcodec/libavcodec.a" || $BUILD_TAGS == *"debug-video"* ]]; then - cd "$ROOT/ffmpeg" - make -j$NPROC - make -j$NPROC install -fi +echo 'WARNING: downloading and executing lpms/install_ffmpeg.sh, use it directly in case of issues' +curl https://raw.githubusercontent.com/livepeer/lpms/5b7b9f5e831f041c6cf707bbaad7b5503c2f138d/install_ffmpeg.sh | bash -s $1 diff --git a/monitor/census.go b/monitor/census.go index 2f883d5e4..12ab13327 100644 --- a/monitor/census.go +++ b/monitor/census.go @@ -1629,14 +1629,12 @@ func Reserve(sender string, reserve *big.Int) { } func MaxTranscodingPrice(maxPrice *big.Rat) { - floatWei, ok := maxPrice.Float64() - if ok { - if err := stats.RecordWithTags(census.ctx, - []tag.Mutator{tag.Insert(census.kSender, "max")}, - census.mTranscodingPrice.M(floatWei)); err != nil { + floatWei, _ := maxPrice.Float64() + if err := stats.RecordWithTags(census.ctx, + []tag.Mutator{tag.Insert(census.kSender, "max")}, + census.mTranscodingPrice.M(floatWei)); err != nil { - glog.Errorf("Error recording metrics err=%q", err) - } + glog.Errorf("Error recording metrics err=%q", err) } } @@ -1750,15 +1748,13 @@ func MaxGasPrice(maxGasPrice *big.Int) { // TranscodingPrice records the last transcoding price func TranscodingPrice(sender string, price *big.Rat) { - floatWei, ok := price.Float64() - if ok { - stats.Record(census.ctx, census.mTranscodingPrice.M(floatWei)) - if err := stats.RecordWithTags(census.ctx, - []tag.Mutator{tag.Insert(census.kSender, sender)}, - census.mTranscodingPrice.M(floatWei)); err != nil { + floatWei, _ := price.Float64() + stats.Record(census.ctx, census.mTranscodingPrice.M(floatWei)) + if err := stats.RecordWithTags(census.ctx, + []tag.Mutator{tag.Insert(census.kSender, sender)}, + census.mTranscodingPrice.M(floatWei)); err != nil { - glog.Errorf("Error recording metrics err=%q", err) - } + glog.Errorf("Error recording metrics err=%q", err) } } diff --git a/net/lp_rpc.pb.go b/net/lp_rpc.pb.go index 0ccebb681..461cbfb79 100644 --- a/net/lp_rpc.pb.go +++ b/net/lp_rpc.pb.go @@ -1,24 +1,24 @@ // Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.1 -// protoc v4.25.2 // source: net/lp_rpc.proto package net import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" ) -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package type OSInfo_StorageType int32 @@ -28,45 +28,24 @@ const ( OSInfo_GOOGLE OSInfo_StorageType = 2 ) -// Enum value maps for OSInfo_StorageType. -var ( - OSInfo_StorageType_name = map[int32]string{ - 0: "DIRECT", - 1: "S3", - 2: "GOOGLE", - } - OSInfo_StorageType_value = map[string]int32{ - "DIRECT": 0, - "S3": 1, - "GOOGLE": 2, - } -) - -func (x OSInfo_StorageType) Enum() *OSInfo_StorageType { - p := new(OSInfo_StorageType) - *p = x - return p -} - -func (x OSInfo_StorageType) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (OSInfo_StorageType) Descriptor() protoreflect.EnumDescriptor { - return file_net_lp_rpc_proto_enumTypes[0].Descriptor() +var OSInfo_StorageType_name = map[int32]string{ + 0: "DIRECT", + 1: "S3", + 2: "GOOGLE", } -func (OSInfo_StorageType) Type() protoreflect.EnumType { - return &file_net_lp_rpc_proto_enumTypes[0] +var OSInfo_StorageType_value = map[string]int32{ + "DIRECT": 0, + "S3": 1, + "GOOGLE": 2, } -func (x OSInfo_StorageType) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) +func (x OSInfo_StorageType) String() string { + return proto.EnumName(OSInfo_StorageType_name, int32(x)) } -// Deprecated: Use OSInfo_StorageType.Descriptor instead. func (OSInfo_StorageType) EnumDescriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{4, 0} + return fileDescriptor_034e29c79f9ba827, []int{4, 0} } // Desired output format @@ -77,43 +56,22 @@ const ( VideoProfile_MP4 VideoProfile_Format = 1 ) -// Enum value maps for VideoProfile_Format. -var ( - VideoProfile_Format_name = map[int32]string{ - 0: "MPEGTS", - 1: "MP4", - } - VideoProfile_Format_value = map[string]int32{ - "MPEGTS": 0, - "MP4": 1, - } -) - -func (x VideoProfile_Format) Enum() *VideoProfile_Format { - p := new(VideoProfile_Format) - *p = x - return p -} - -func (x VideoProfile_Format) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (VideoProfile_Format) Descriptor() protoreflect.EnumDescriptor { - return file_net_lp_rpc_proto_enumTypes[1].Descriptor() +var VideoProfile_Format_name = map[int32]string{ + 0: "MPEGTS", + 1: "MP4", } -func (VideoProfile_Format) Type() protoreflect.EnumType { - return &file_net_lp_rpc_proto_enumTypes[1] +var VideoProfile_Format_value = map[string]int32{ + "MPEGTS": 0, + "MP4": 1, } -func (x VideoProfile_Format) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) +func (x VideoProfile_Format) String() string { + return proto.EnumName(VideoProfile_Format_name, int32(x)) } -// Deprecated: Use VideoProfile_Format.Descriptor instead. func (VideoProfile_Format) EnumDescriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{12, 0} + return fileDescriptor_034e29c79f9ba827, []int{12, 0} } type VideoProfile_Profile int32 @@ -126,49 +84,28 @@ const ( VideoProfile_H264_CONSTRAINED_HIGH VideoProfile_Profile = 4 ) -// Enum value maps for VideoProfile_Profile. -var ( - VideoProfile_Profile_name = map[int32]string{ - 0: "ENCODER_DEFAULT", - 1: "H264_BASELINE", - 2: "H264_MAIN", - 3: "H264_HIGH", - 4: "H264_CONSTRAINED_HIGH", - } - VideoProfile_Profile_value = map[string]int32{ - "ENCODER_DEFAULT": 0, - "H264_BASELINE": 1, - "H264_MAIN": 2, - "H264_HIGH": 3, - "H264_CONSTRAINED_HIGH": 4, - } -) - -func (x VideoProfile_Profile) Enum() *VideoProfile_Profile { - p := new(VideoProfile_Profile) - *p = x - return p -} - -func (x VideoProfile_Profile) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (VideoProfile_Profile) Descriptor() protoreflect.EnumDescriptor { - return file_net_lp_rpc_proto_enumTypes[2].Descriptor() +var VideoProfile_Profile_name = map[int32]string{ + 0: "ENCODER_DEFAULT", + 1: "H264_BASELINE", + 2: "H264_MAIN", + 3: "H264_HIGH", + 4: "H264_CONSTRAINED_HIGH", } -func (VideoProfile_Profile) Type() protoreflect.EnumType { - return &file_net_lp_rpc_proto_enumTypes[2] +var VideoProfile_Profile_value = map[string]int32{ + "ENCODER_DEFAULT": 0, + "H264_BASELINE": 1, + "H264_MAIN": 2, + "H264_HIGH": 3, + "H264_CONSTRAINED_HIGH": 4, } -func (x VideoProfile_Profile) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) +func (x VideoProfile_Profile) String() string { + return proto.EnumName(VideoProfile_Profile_name, int32(x)) } -// Deprecated: Use VideoProfile_Profile.Descriptor instead. func (VideoProfile_Profile) EnumDescriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{12, 1} + return fileDescriptor_034e29c79f9ba827, []int{12, 1} } type VideoProfile_VideoCodec int32 @@ -180,47 +117,26 @@ const ( VideoProfile_VP9 VideoProfile_VideoCodec = 3 ) -// Enum value maps for VideoProfile_VideoCodec. -var ( - VideoProfile_VideoCodec_name = map[int32]string{ - 0: "H264", - 1: "H265", - 2: "VP8", - 3: "VP9", - } - VideoProfile_VideoCodec_value = map[string]int32{ - "H264": 0, - "H265": 1, - "VP8": 2, - "VP9": 3, - } -) - -func (x VideoProfile_VideoCodec) Enum() *VideoProfile_VideoCodec { - p := new(VideoProfile_VideoCodec) - *p = x - return p -} - -func (x VideoProfile_VideoCodec) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (VideoProfile_VideoCodec) Descriptor() protoreflect.EnumDescriptor { - return file_net_lp_rpc_proto_enumTypes[3].Descriptor() +var VideoProfile_VideoCodec_name = map[int32]string{ + 0: "H264", + 1: "H265", + 2: "VP8", + 3: "VP9", } -func (VideoProfile_VideoCodec) Type() protoreflect.EnumType { - return &file_net_lp_rpc_proto_enumTypes[3] +var VideoProfile_VideoCodec_value = map[string]int32{ + "H264": 0, + "H265": 1, + "VP8": 2, + "VP9": 3, } -func (x VideoProfile_VideoCodec) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) +func (x VideoProfile_VideoCodec) String() string { + return proto.EnumName(VideoProfile_VideoCodec_name, int32(x)) } -// Deprecated: Use VideoProfile_VideoCodec.Descriptor instead. func (VideoProfile_VideoCodec) EnumDescriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{12, 2} + return fileDescriptor_034e29c79f9ba827, []int{12, 2} } type VideoProfile_ChromaSubsampling int32 @@ -231,246 +147,194 @@ const ( VideoProfile_CHROMA_444 VideoProfile_ChromaSubsampling = 2 ) -// Enum value maps for VideoProfile_ChromaSubsampling. -var ( - VideoProfile_ChromaSubsampling_name = map[int32]string{ - 0: "CHROMA_420", - 1: "CHROMA_422", - 2: "CHROMA_444", - } - VideoProfile_ChromaSubsampling_value = map[string]int32{ - "CHROMA_420": 0, - "CHROMA_422": 1, - "CHROMA_444": 2, - } -) - -func (x VideoProfile_ChromaSubsampling) Enum() *VideoProfile_ChromaSubsampling { - p := new(VideoProfile_ChromaSubsampling) - *p = x - return p -} - -func (x VideoProfile_ChromaSubsampling) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (VideoProfile_ChromaSubsampling) Descriptor() protoreflect.EnumDescriptor { - return file_net_lp_rpc_proto_enumTypes[4].Descriptor() +var VideoProfile_ChromaSubsampling_name = map[int32]string{ + 0: "CHROMA_420", + 1: "CHROMA_422", + 2: "CHROMA_444", } -func (VideoProfile_ChromaSubsampling) Type() protoreflect.EnumType { - return &file_net_lp_rpc_proto_enumTypes[4] +var VideoProfile_ChromaSubsampling_value = map[string]int32{ + "CHROMA_420": 0, + "CHROMA_422": 1, + "CHROMA_444": 2, } -func (x VideoProfile_ChromaSubsampling) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) +func (x VideoProfile_ChromaSubsampling) String() string { + return proto.EnumName(VideoProfile_ChromaSubsampling_name, int32(x)) } -// Deprecated: Use VideoProfile_ChromaSubsampling.Descriptor instead. func (VideoProfile_ChromaSubsampling) EnumDescriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{12, 3} + return fileDescriptor_034e29c79f9ba827, []int{12, 3} } type PingPong struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - // Implementation defined - Value []byte `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` + Value []byte `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *PingPong) Reset() { - *x = PingPong{} - if protoimpl.UnsafeEnabled { - mi := &file_net_lp_rpc_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *PingPong) Reset() { *m = PingPong{} } +func (m *PingPong) String() string { return proto.CompactTextString(m) } +func (*PingPong) ProtoMessage() {} +func (*PingPong) Descriptor() ([]byte, []int) { + return fileDescriptor_034e29c79f9ba827, []int{0} } -func (x *PingPong) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *PingPong) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PingPong.Unmarshal(m, b) } - -func (*PingPong) ProtoMessage() {} - -func (x *PingPong) ProtoReflect() protoreflect.Message { - mi := &file_net_lp_rpc_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) +func (m *PingPong) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PingPong.Marshal(b, m, deterministic) } - -// Deprecated: Use PingPong.ProtoReflect.Descriptor instead. -func (*PingPong) Descriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{0} +func (m *PingPong) XXX_Merge(src proto.Message) { + xxx_messageInfo_PingPong.Merge(m, src) +} +func (m *PingPong) XXX_Size() int { + return xxx_messageInfo_PingPong.Size(m) +} +func (m *PingPong) XXX_DiscardUnknown() { + xxx_messageInfo_PingPong.DiscardUnknown(m) } -func (x *PingPong) GetValue() []byte { - if x != nil { - return x.Value +var xxx_messageInfo_PingPong proto.InternalMessageInfo + +func (m *PingPong) GetValue() []byte { + if m != nil { + return m.Value } return nil } // sent by Broadcaster to Orchestrator to terminate the transcoding session and free resources (used for verification sessions) type EndTranscodingSessionRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - // Data for transcoding authentication - AuthToken *AuthToken `protobuf:"bytes,1,opt,name=auth_token,json=authToken,proto3" json:"auth_token,omitempty"` + AuthToken *AuthToken `protobuf:"bytes,1,opt,name=auth_token,json=authToken,proto3" json:"auth_token,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *EndTranscodingSessionRequest) Reset() { - *x = EndTranscodingSessionRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_net_lp_rpc_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *EndTranscodingSessionRequest) Reset() { *m = EndTranscodingSessionRequest{} } +func (m *EndTranscodingSessionRequest) String() string { return proto.CompactTextString(m) } +func (*EndTranscodingSessionRequest) ProtoMessage() {} +func (*EndTranscodingSessionRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_034e29c79f9ba827, []int{1} } -func (x *EndTranscodingSessionRequest) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *EndTranscodingSessionRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EndTranscodingSessionRequest.Unmarshal(m, b) } - -func (*EndTranscodingSessionRequest) ProtoMessage() {} - -func (x *EndTranscodingSessionRequest) ProtoReflect() protoreflect.Message { - mi := &file_net_lp_rpc_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) +func (m *EndTranscodingSessionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EndTranscodingSessionRequest.Marshal(b, m, deterministic) } - -// Deprecated: Use EndTranscodingSessionRequest.ProtoReflect.Descriptor instead. -func (*EndTranscodingSessionRequest) Descriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{1} +func (m *EndTranscodingSessionRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_EndTranscodingSessionRequest.Merge(m, src) +} +func (m *EndTranscodingSessionRequest) XXX_Size() int { + return xxx_messageInfo_EndTranscodingSessionRequest.Size(m) +} +func (m *EndTranscodingSessionRequest) XXX_DiscardUnknown() { + xxx_messageInfo_EndTranscodingSessionRequest.DiscardUnknown(m) } -func (x *EndTranscodingSessionRequest) GetAuthToken() *AuthToken { - if x != nil { - return x.AuthToken +var xxx_messageInfo_EndTranscodingSessionRequest proto.InternalMessageInfo + +func (m *EndTranscodingSessionRequest) GetAuthToken() *AuthToken { + if m != nil { + return m.AuthToken } return nil } type EndTranscodingSessionResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *EndTranscodingSessionResponse) Reset() { - *x = EndTranscodingSessionResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_net_lp_rpc_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *EndTranscodingSessionResponse) Reset() { *m = EndTranscodingSessionResponse{} } +func (m *EndTranscodingSessionResponse) String() string { return proto.CompactTextString(m) } +func (*EndTranscodingSessionResponse) ProtoMessage() {} +func (*EndTranscodingSessionResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_034e29c79f9ba827, []int{2} } -func (x *EndTranscodingSessionResponse) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *EndTranscodingSessionResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EndTranscodingSessionResponse.Unmarshal(m, b) } - -func (*EndTranscodingSessionResponse) ProtoMessage() {} - -func (x *EndTranscodingSessionResponse) ProtoReflect() protoreflect.Message { - mi := &file_net_lp_rpc_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) +func (m *EndTranscodingSessionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EndTranscodingSessionResponse.Marshal(b, m, deterministic) } - -// Deprecated: Use EndTranscodingSessionResponse.ProtoReflect.Descriptor instead. -func (*EndTranscodingSessionResponse) Descriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{2} +func (m *EndTranscodingSessionResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_EndTranscodingSessionResponse.Merge(m, src) +} +func (m *EndTranscodingSessionResponse) XXX_Size() int { + return xxx_messageInfo_EndTranscodingSessionResponse.Size(m) } +func (m *EndTranscodingSessionResponse) XXX_DiscardUnknown() { + xxx_messageInfo_EndTranscodingSessionResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_EndTranscodingSessionResponse proto.InternalMessageInfo // This request is sent by the broadcaster in `GetTranscoder` to request // information on which transcoder to use. type OrchestratorRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - // Ethereum address of the broadcaster Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` // Broadcaster's signature over its address Sig []byte `protobuf:"bytes,2,opt,name=sig,proto3" json:"sig,omitempty"` // Features and constraints required by the broadcaster - Capabilities *Capabilities `protobuf:"bytes,3,opt,name=capabilities,proto3" json:"capabilities,omitempty"` + Capabilities *Capabilities `protobuf:"bytes,3,opt,name=capabilities,proto3" json:"capabilities,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *OrchestratorRequest) Reset() { - *x = OrchestratorRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_net_lp_rpc_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *OrchestratorRequest) Reset() { *m = OrchestratorRequest{} } +func (m *OrchestratorRequest) String() string { return proto.CompactTextString(m) } +func (*OrchestratorRequest) ProtoMessage() {} +func (*OrchestratorRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_034e29c79f9ba827, []int{3} } -func (x *OrchestratorRequest) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *OrchestratorRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_OrchestratorRequest.Unmarshal(m, b) } - -func (*OrchestratorRequest) ProtoMessage() {} - -func (x *OrchestratorRequest) ProtoReflect() protoreflect.Message { - mi := &file_net_lp_rpc_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) +func (m *OrchestratorRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_OrchestratorRequest.Marshal(b, m, deterministic) } - -// Deprecated: Use OrchestratorRequest.ProtoReflect.Descriptor instead. -func (*OrchestratorRequest) Descriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{3} +func (m *OrchestratorRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_OrchestratorRequest.Merge(m, src) +} +func (m *OrchestratorRequest) XXX_Size() int { + return xxx_messageInfo_OrchestratorRequest.Size(m) +} +func (m *OrchestratorRequest) XXX_DiscardUnknown() { + xxx_messageInfo_OrchestratorRequest.DiscardUnknown(m) } -func (x *OrchestratorRequest) GetAddress() []byte { - if x != nil { - return x.Address +var xxx_messageInfo_OrchestratorRequest proto.InternalMessageInfo + +func (m *OrchestratorRequest) GetAddress() []byte { + if m != nil { + return m.Address } return nil } -func (x *OrchestratorRequest) GetSig() []byte { - if x != nil { - return x.Sig +func (m *OrchestratorRequest) GetSig() []byte { + if m != nil { + return m.Sig } return nil } -func (x *OrchestratorRequest) GetCapabilities() *Capabilities { - if x != nil { - return x.Capabilities +func (m *OrchestratorRequest) GetCapabilities() *Capabilities { + if m != nil { + return m.Capabilities } return nil } @@ -478,66 +342,54 @@ func (x *OrchestratorRequest) GetCapabilities() *Capabilities { // OSInfo needed to negotiate storages that will be used. // It carries info needed to write to the storage. type OSInfo struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - // Storage type: direct, s3, ipfs. - StorageType OSInfo_StorageType `protobuf:"varint,1,opt,name=storageType,proto3,enum=net.OSInfo_StorageType" json:"storageType,omitempty"` - S3Info *S3OSInfo `protobuf:"bytes,16,opt,name=s3info,proto3" json:"s3info,omitempty"` + StorageType OSInfo_StorageType `protobuf:"varint,1,opt,name=storageType,proto3,enum=net.OSInfo_StorageType" json:"storageType,omitempty"` + S3Info *S3OSInfo `protobuf:"bytes,16,opt,name=s3info,proto3" json:"s3info,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *OSInfo) Reset() { - *x = OSInfo{} - if protoimpl.UnsafeEnabled { - mi := &file_net_lp_rpc_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *OSInfo) Reset() { *m = OSInfo{} } +func (m *OSInfo) String() string { return proto.CompactTextString(m) } +func (*OSInfo) ProtoMessage() {} +func (*OSInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_034e29c79f9ba827, []int{4} } -func (x *OSInfo) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *OSInfo) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_OSInfo.Unmarshal(m, b) } - -func (*OSInfo) ProtoMessage() {} - -func (x *OSInfo) ProtoReflect() protoreflect.Message { - mi := &file_net_lp_rpc_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) +func (m *OSInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_OSInfo.Marshal(b, m, deterministic) } - -// Deprecated: Use OSInfo.ProtoReflect.Descriptor instead. -func (*OSInfo) Descriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{4} +func (m *OSInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_OSInfo.Merge(m, src) +} +func (m *OSInfo) XXX_Size() int { + return xxx_messageInfo_OSInfo.Size(m) +} +func (m *OSInfo) XXX_DiscardUnknown() { + xxx_messageInfo_OSInfo.DiscardUnknown(m) } -func (x *OSInfo) GetStorageType() OSInfo_StorageType { - if x != nil { - return x.StorageType +var xxx_messageInfo_OSInfo proto.InternalMessageInfo + +func (m *OSInfo) GetStorageType() OSInfo_StorageType { + if m != nil { + return m.StorageType } return OSInfo_DIRECT } -func (x *OSInfo) GetS3Info() *S3OSInfo { - if x != nil { - return x.S3Info +func (m *OSInfo) GetS3Info() *S3OSInfo { + if m != nil { + return m.S3Info } return nil } type S3OSInfo struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - // Host to use to connect to S3 Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` // Key (prefix) to use when uploading the object. @@ -549,223 +401,338 @@ type S3OSInfo struct { // Needed for POST policy. Credential string `protobuf:"bytes,5,opt,name=credential,proto3" json:"credential,omitempty"` // Needed for POST policy. - XAmzDate string `protobuf:"bytes,6,opt,name=xAmzDate,proto3" json:"xAmzDate,omitempty"` + XAmzDate string `protobuf:"bytes,6,opt,name=xAmzDate,proto3" json:"xAmzDate,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *S3OSInfo) Reset() { - *x = S3OSInfo{} - if protoimpl.UnsafeEnabled { - mi := &file_net_lp_rpc_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *S3OSInfo) Reset() { *m = S3OSInfo{} } +func (m *S3OSInfo) String() string { return proto.CompactTextString(m) } +func (*S3OSInfo) ProtoMessage() {} +func (*S3OSInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_034e29c79f9ba827, []int{5} } -func (x *S3OSInfo) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *S3OSInfo) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_S3OSInfo.Unmarshal(m, b) } - -func (*S3OSInfo) ProtoMessage() {} - -func (x *S3OSInfo) ProtoReflect() protoreflect.Message { - mi := &file_net_lp_rpc_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) +func (m *S3OSInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_S3OSInfo.Marshal(b, m, deterministic) } - -// Deprecated: Use S3OSInfo.ProtoReflect.Descriptor instead. -func (*S3OSInfo) Descriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{5} +func (m *S3OSInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_S3OSInfo.Merge(m, src) +} +func (m *S3OSInfo) XXX_Size() int { + return xxx_messageInfo_S3OSInfo.Size(m) } +func (m *S3OSInfo) XXX_DiscardUnknown() { + xxx_messageInfo_S3OSInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_S3OSInfo proto.InternalMessageInfo -func (x *S3OSInfo) GetHost() string { - if x != nil { - return x.Host +func (m *S3OSInfo) GetHost() string { + if m != nil { + return m.Host } return "" } -func (x *S3OSInfo) GetKey() string { - if x != nil { - return x.Key +func (m *S3OSInfo) GetKey() string { + if m != nil { + return m.Key } return "" } -func (x *S3OSInfo) GetPolicy() string { - if x != nil { - return x.Policy +func (m *S3OSInfo) GetPolicy() string { + if m != nil { + return m.Policy } return "" } -func (x *S3OSInfo) GetSignature() string { - if x != nil { - return x.Signature +func (m *S3OSInfo) GetSignature() string { + if m != nil { + return m.Signature } return "" } -func (x *S3OSInfo) GetCredential() string { - if x != nil { - return x.Credential +func (m *S3OSInfo) GetCredential() string { + if m != nil { + return m.Credential } return "" } -func (x *S3OSInfo) GetXAmzDate() string { - if x != nil { - return x.XAmzDate +func (m *S3OSInfo) GetXAmzDate() string { + if m != nil { + return m.XAmzDate } return "" } // PriceInfo conveys pricing info for transcoding services type PriceInfo struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - // price in wei PricePerUnit int64 `protobuf:"varint,1,opt,name=pricePerUnit,proto3" json:"pricePerUnit,omitempty"` // Pixels covered in the price // Set price to 1 wei and pixelsPerUnit > 1 to have a smaller price granularity per pixel than 1 wei - PixelsPerUnit int64 `protobuf:"varint,2,opt,name=pixelsPerUnit,proto3" json:"pixelsPerUnit,omitempty"` + PixelsPerUnit int64 `protobuf:"varint,2,opt,name=pixelsPerUnit,proto3" json:"pixelsPerUnit,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *PriceInfo) Reset() { - *x = PriceInfo{} - if protoimpl.UnsafeEnabled { - mi := &file_net_lp_rpc_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *PriceInfo) Reset() { *m = PriceInfo{} } +func (m *PriceInfo) String() string { return proto.CompactTextString(m) } +func (*PriceInfo) ProtoMessage() {} +func (*PriceInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_034e29c79f9ba827, []int{6} } -func (x *PriceInfo) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *PriceInfo) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PriceInfo.Unmarshal(m, b) } - -func (*PriceInfo) ProtoMessage() {} - -func (x *PriceInfo) ProtoReflect() protoreflect.Message { - mi := &file_net_lp_rpc_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) +func (m *PriceInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PriceInfo.Marshal(b, m, deterministic) } - -// Deprecated: Use PriceInfo.ProtoReflect.Descriptor instead. -func (*PriceInfo) Descriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{6} +func (m *PriceInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_PriceInfo.Merge(m, src) +} +func (m *PriceInfo) XXX_Size() int { + return xxx_messageInfo_PriceInfo.Size(m) +} +func (m *PriceInfo) XXX_DiscardUnknown() { + xxx_messageInfo_PriceInfo.DiscardUnknown(m) } -func (x *PriceInfo) GetPricePerUnit() int64 { - if x != nil { - return x.PricePerUnit +var xxx_messageInfo_PriceInfo proto.InternalMessageInfo + +func (m *PriceInfo) GetPricePerUnit() int64 { + if m != nil { + return m.PricePerUnit } return 0 } -func (x *PriceInfo) GetPixelsPerUnit() int64 { - if x != nil { - return x.PixelsPerUnit +func (m *PriceInfo) GetPixelsPerUnit() int64 { + if m != nil { + return m.PixelsPerUnit } return 0 } type Capabilities struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - // Bit string of supported features - one bit per feature Bitstring []uint64 `protobuf:"varint,1,rep,packed,name=bitstring,proto3" json:"bitstring,omitempty"` // Bit string of features that are required to be supported Mandatories []uint64 `protobuf:"varint,2,rep,packed,name=mandatories,proto3" json:"mandatories,omitempty"` // Capacity corresponding to each capability - Capacities map[uint32]uint32 `protobuf:"bytes,3,rep,name=capacities,proto3" json:"capacities,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` - Constraints map[uint32]*Capabilities_Constraints `protobuf:"bytes,4,rep,name=constraints,proto3" json:"constraints,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Capacities map[uint32]uint32 `protobuf:"bytes,3,rep,name=capacities,proto3" json:"capacities,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` + Version string `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` + Constraints *Capabilities_Constraints `protobuf:"bytes,5,opt,name=constraints,proto3" json:"constraints,omitempty"` + CapabilityConstraints map[uint32]*Capabilities_CapabilityConstraints `protobuf:"bytes,6,rep,name=capabilityConstraints,proto3" json:"capabilityConstraints,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Capabilities) Reset() { *m = Capabilities{} } +func (m *Capabilities) String() string { return proto.CompactTextString(m) } +func (*Capabilities) ProtoMessage() {} +func (*Capabilities) Descriptor() ([]byte, []int) { + return fileDescriptor_034e29c79f9ba827, []int{7} } -func (x *Capabilities) Reset() { - *x = Capabilities{} - if protoimpl.UnsafeEnabled { - mi := &file_net_lp_rpc_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *Capabilities) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Capabilities.Unmarshal(m, b) } - -func (x *Capabilities) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *Capabilities) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Capabilities.Marshal(b, m, deterministic) +} +func (m *Capabilities) XXX_Merge(src proto.Message) { + xxx_messageInfo_Capabilities.Merge(m, src) +} +func (m *Capabilities) XXX_Size() int { + return xxx_messageInfo_Capabilities.Size(m) +} +func (m *Capabilities) XXX_DiscardUnknown() { + xxx_messageInfo_Capabilities.DiscardUnknown(m) } -func (*Capabilities) ProtoMessage() {} +var xxx_messageInfo_Capabilities proto.InternalMessageInfo -func (x *Capabilities) ProtoReflect() protoreflect.Message { - mi := &file_net_lp_rpc_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (m *Capabilities) GetBitstring() []uint64 { + if m != nil { + return m.Bitstring } - return mi.MessageOf(x) + return nil } -// Deprecated: Use Capabilities.ProtoReflect.Descriptor instead. -func (*Capabilities) Descriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{7} +func (m *Capabilities) GetMandatories() []uint64 { + if m != nil { + return m.Mandatories + } + return nil } -func (x *Capabilities) GetBitstring() []uint64 { - if x != nil { - return x.Bitstring +func (m *Capabilities) GetCapacities() map[uint32]uint32 { + if m != nil { + return m.Capacities } return nil } -func (x *Capabilities) GetMandatories() []uint64 { - if x != nil { - return x.Mandatories +func (m *Capabilities) GetVersion() string { + if m != nil { + return m.Version + } + return "" +} + +func (m *Capabilities) GetConstraints() *Capabilities_Constraints { + if m != nil { + return m.Constraints } return nil } -func (x *Capabilities) GetCapacities() map[uint32]uint32 { - if x != nil { - return x.Capacities +func (m *Capabilities) GetCapabilityConstraints() map[uint32]*Capabilities_CapabilityConstraints { + if m != nil { + return m.CapabilityConstraints } return nil } -func (x *Capabilities) GetConstraints() map[uint32]*Capabilities_Constraints { - if x != nil { - return x.Constraints +// Non-binary general constraints. +type Capabilities_Constraints struct { + MinVersion string `protobuf:"bytes,1,opt,name=minVersion,proto3" json:"minVersion,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Capabilities_Constraints) Reset() { *m = Capabilities_Constraints{} } +func (m *Capabilities_Constraints) String() string { return proto.CompactTextString(m) } +func (*Capabilities_Constraints) ProtoMessage() {} +func (*Capabilities_Constraints) Descriptor() ([]byte, []int) { + return fileDescriptor_034e29c79f9ba827, []int{7, 1} +} + +func (m *Capabilities_Constraints) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Capabilities_Constraints.Unmarshal(m, b) +} +func (m *Capabilities_Constraints) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Capabilities_Constraints.Marshal(b, m, deterministic) +} +func (m *Capabilities_Constraints) XXX_Merge(src proto.Message) { + xxx_messageInfo_Capabilities_Constraints.Merge(m, src) +} +func (m *Capabilities_Constraints) XXX_Size() int { + return xxx_messageInfo_Capabilities_Constraints.Size(m) +} +func (m *Capabilities_Constraints) XXX_DiscardUnknown() { + xxx_messageInfo_Capabilities_Constraints.DiscardUnknown(m) +} + +var xxx_messageInfo_Capabilities_Constraints proto.InternalMessageInfo + +func (m *Capabilities_Constraints) GetMinVersion() string { + if m != nil { + return m.MinVersion + } + return "" +} + +// Non-binary capability constraints, such as supported ranges. +type Capabilities_CapabilityConstraints struct { + Models map[string]*Capabilities_CapabilityConstraints_ModelConstraint `protobuf:"bytes,1,rep,name=models,proto3" json:"models,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Capabilities_CapabilityConstraints) Reset() { *m = Capabilities_CapabilityConstraints{} } +func (m *Capabilities_CapabilityConstraints) String() string { return proto.CompactTextString(m) } +func (*Capabilities_CapabilityConstraints) ProtoMessage() {} +func (*Capabilities_CapabilityConstraints) Descriptor() ([]byte, []int) { + return fileDescriptor_034e29c79f9ba827, []int{7, 2} +} + +func (m *Capabilities_CapabilityConstraints) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Capabilities_CapabilityConstraints.Unmarshal(m, b) +} +func (m *Capabilities_CapabilityConstraints) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Capabilities_CapabilityConstraints.Marshal(b, m, deterministic) +} +func (m *Capabilities_CapabilityConstraints) XXX_Merge(src proto.Message) { + xxx_messageInfo_Capabilities_CapabilityConstraints.Merge(m, src) +} +func (m *Capabilities_CapabilityConstraints) XXX_Size() int { + return xxx_messageInfo_Capabilities_CapabilityConstraints.Size(m) +} +func (m *Capabilities_CapabilityConstraints) XXX_DiscardUnknown() { + xxx_messageInfo_Capabilities_CapabilityConstraints.DiscardUnknown(m) +} + +var xxx_messageInfo_Capabilities_CapabilityConstraints proto.InternalMessageInfo + +func (m *Capabilities_CapabilityConstraints) GetModels() map[string]*Capabilities_CapabilityConstraints_ModelConstraint { + if m != nil { + return m.Models } return nil } +type Capabilities_CapabilityConstraints_ModelConstraint struct { + Warm bool `protobuf:"varint,1,opt,name=warm,proto3" json:"warm,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Capabilities_CapabilityConstraints_ModelConstraint) Reset() { + *m = Capabilities_CapabilityConstraints_ModelConstraint{} +} +func (m *Capabilities_CapabilityConstraints_ModelConstraint) String() string { + return proto.CompactTextString(m) +} +func (*Capabilities_CapabilityConstraints_ModelConstraint) ProtoMessage() {} +func (*Capabilities_CapabilityConstraints_ModelConstraint) Descriptor() ([]byte, []int) { + return fileDescriptor_034e29c79f9ba827, []int{7, 2, 0} +} + +func (m *Capabilities_CapabilityConstraints_ModelConstraint) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Capabilities_CapabilityConstraints_ModelConstraint.Unmarshal(m, b) +} +func (m *Capabilities_CapabilityConstraints_ModelConstraint) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Capabilities_CapabilityConstraints_ModelConstraint.Marshal(b, m, deterministic) +} +func (m *Capabilities_CapabilityConstraints_ModelConstraint) XXX_Merge(src proto.Message) { + xxx_messageInfo_Capabilities_CapabilityConstraints_ModelConstraint.Merge(m, src) +} +func (m *Capabilities_CapabilityConstraints_ModelConstraint) XXX_Size() int { + return xxx_messageInfo_Capabilities_CapabilityConstraints_ModelConstraint.Size(m) +} +func (m *Capabilities_CapabilityConstraints_ModelConstraint) XXX_DiscardUnknown() { + xxx_messageInfo_Capabilities_CapabilityConstraints_ModelConstraint.DiscardUnknown(m) +} + +var xxx_messageInfo_Capabilities_CapabilityConstraints_ModelConstraint proto.InternalMessageInfo + +func (m *Capabilities_CapabilityConstraints_ModelConstraint) GetWarm() bool { + if m != nil { + return m.Warm + } + return false +} + // The orchestrator sends this in response to `GetOrchestrator`, containing // miscellaneous data related to the job. type OrchestratorInfo struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - // URI of the transcoder to use for submitting segments. Transcoder string `protobuf:"bytes,1,opt,name=transcoder,proto3" json:"transcoder,omitempty"` // Parameters for probabilistic micropayment tickets @@ -779,164 +746,148 @@ type OrchestratorInfo struct { // Data for transcoding authentication AuthToken *AuthToken `protobuf:"bytes,6,opt,name=auth_token,json=authToken,proto3" json:"auth_token,omitempty"` // Orchestrator returns info about own input object storage, if it wants it to be used. - Storage []*OSInfo `protobuf:"bytes,32,rep,name=storage,proto3" json:"storage,omitempty"` + Storage []*OSInfo `protobuf:"bytes,32,rep,name=storage,proto3" json:"storage,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *OrchestratorInfo) Reset() { - *x = OrchestratorInfo{} - if protoimpl.UnsafeEnabled { - mi := &file_net_lp_rpc_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *OrchestratorInfo) Reset() { *m = OrchestratorInfo{} } +func (m *OrchestratorInfo) String() string { return proto.CompactTextString(m) } +func (*OrchestratorInfo) ProtoMessage() {} +func (*OrchestratorInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_034e29c79f9ba827, []int{8} } -func (x *OrchestratorInfo) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *OrchestratorInfo) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_OrchestratorInfo.Unmarshal(m, b) } - -func (*OrchestratorInfo) ProtoMessage() {} - -func (x *OrchestratorInfo) ProtoReflect() protoreflect.Message { - mi := &file_net_lp_rpc_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) +func (m *OrchestratorInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_OrchestratorInfo.Marshal(b, m, deterministic) } - -// Deprecated: Use OrchestratorInfo.ProtoReflect.Descriptor instead. -func (*OrchestratorInfo) Descriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{8} +func (m *OrchestratorInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_OrchestratorInfo.Merge(m, src) +} +func (m *OrchestratorInfo) XXX_Size() int { + return xxx_messageInfo_OrchestratorInfo.Size(m) +} +func (m *OrchestratorInfo) XXX_DiscardUnknown() { + xxx_messageInfo_OrchestratorInfo.DiscardUnknown(m) } -func (x *OrchestratorInfo) GetTranscoder() string { - if x != nil { - return x.Transcoder +var xxx_messageInfo_OrchestratorInfo proto.InternalMessageInfo + +func (m *OrchestratorInfo) GetTranscoder() string { + if m != nil { + return m.Transcoder } return "" } -func (x *OrchestratorInfo) GetTicketParams() *TicketParams { - if x != nil { - return x.TicketParams +func (m *OrchestratorInfo) GetTicketParams() *TicketParams { + if m != nil { + return m.TicketParams } return nil } -func (x *OrchestratorInfo) GetPriceInfo() *PriceInfo { - if x != nil { - return x.PriceInfo +func (m *OrchestratorInfo) GetPriceInfo() *PriceInfo { + if m != nil { + return m.PriceInfo } return nil } -func (x *OrchestratorInfo) GetAddress() []byte { - if x != nil { - return x.Address +func (m *OrchestratorInfo) GetAddress() []byte { + if m != nil { + return m.Address } return nil } -func (x *OrchestratorInfo) GetCapabilities() *Capabilities { - if x != nil { - return x.Capabilities +func (m *OrchestratorInfo) GetCapabilities() *Capabilities { + if m != nil { + return m.Capabilities } return nil } -func (x *OrchestratorInfo) GetAuthToken() *AuthToken { - if x != nil { - return x.AuthToken +func (m *OrchestratorInfo) GetAuthToken() *AuthToken { + if m != nil { + return m.AuthToken } return nil } -func (x *OrchestratorInfo) GetStorage() []*OSInfo { - if x != nil { - return x.Storage +func (m *OrchestratorInfo) GetStorage() []*OSInfo { + if m != nil { + return m.Storage } return nil } // Data for transcoding authentication that is included in the OrchestratorInfo message during discovery type AuthToken struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - // Record used to authenticate for a transcode session // Opaque to the receiver Token []byte `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` // ID of the transcode session that the token is authenticating for SessionId string `protobuf:"bytes,2,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` // Timestamp when the token expires - Expiration int64 `protobuf:"varint,3,opt,name=expiration,proto3" json:"expiration,omitempty"` + Expiration int64 `protobuf:"varint,3,opt,name=expiration,proto3" json:"expiration,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *AuthToken) Reset() { - *x = AuthToken{} - if protoimpl.UnsafeEnabled { - mi := &file_net_lp_rpc_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *AuthToken) Reset() { *m = AuthToken{} } +func (m *AuthToken) String() string { return proto.CompactTextString(m) } +func (*AuthToken) ProtoMessage() {} +func (*AuthToken) Descriptor() ([]byte, []int) { + return fileDescriptor_034e29c79f9ba827, []int{9} } -func (x *AuthToken) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *AuthToken) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AuthToken.Unmarshal(m, b) } - -func (*AuthToken) ProtoMessage() {} - -func (x *AuthToken) ProtoReflect() protoreflect.Message { - mi := &file_net_lp_rpc_proto_msgTypes[9] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) +func (m *AuthToken) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AuthToken.Marshal(b, m, deterministic) } - -// Deprecated: Use AuthToken.ProtoReflect.Descriptor instead. -func (*AuthToken) Descriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{9} +func (m *AuthToken) XXX_Merge(src proto.Message) { + xxx_messageInfo_AuthToken.Merge(m, src) +} +func (m *AuthToken) XXX_Size() int { + return xxx_messageInfo_AuthToken.Size(m) +} +func (m *AuthToken) XXX_DiscardUnknown() { + xxx_messageInfo_AuthToken.DiscardUnknown(m) } -func (x *AuthToken) GetToken() []byte { - if x != nil { - return x.Token +var xxx_messageInfo_AuthToken proto.InternalMessageInfo + +func (m *AuthToken) GetToken() []byte { + if m != nil { + return m.Token } return nil } -func (x *AuthToken) GetSessionId() string { - if x != nil { - return x.SessionId +func (m *AuthToken) GetSessionId() string { + if m != nil { + return m.SessionId } return "" } -func (x *AuthToken) GetExpiration() int64 { - if x != nil { - return x.Expiration +func (m *AuthToken) GetExpiration() int64 { + if m != nil { + return m.Expiration } return 0 } // Data included by the broadcaster when submitting a segment for transcoding. type SegData struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - // Manifest ID this segment belongs to ManifestId []byte `protobuf:"bytes,1,opt,name=manifestId,proto3" json:"manifestId,omitempty"` // Sequence number of the segment to be transcoded @@ -970,210 +921,194 @@ type SegData struct { // Transcoding parameters specific to this segment SegmentParameters *SegParameters `protobuf:"bytes,37,opt,name=segment_parameters,json=segmentParameters,proto3" json:"segment_parameters,omitempty"` // Force HW Session Reinit - ForceSessionReinit bool `protobuf:"varint,38,opt,name=ForceSessionReinit,proto3" json:"ForceSessionReinit,omitempty"` + ForceSessionReinit bool `protobuf:"varint,38,opt,name=ForceSessionReinit,proto3" json:"ForceSessionReinit,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *SegData) Reset() { - *x = SegData{} - if protoimpl.UnsafeEnabled { - mi := &file_net_lp_rpc_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *SegData) Reset() { *m = SegData{} } +func (m *SegData) String() string { return proto.CompactTextString(m) } +func (*SegData) ProtoMessage() {} +func (*SegData) Descriptor() ([]byte, []int) { + return fileDescriptor_034e29c79f9ba827, []int{10} } -func (x *SegData) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *SegData) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SegData.Unmarshal(m, b) } - -func (*SegData) ProtoMessage() {} - -func (x *SegData) ProtoReflect() protoreflect.Message { - mi := &file_net_lp_rpc_proto_msgTypes[10] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) +func (m *SegData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SegData.Marshal(b, m, deterministic) } - -// Deprecated: Use SegData.ProtoReflect.Descriptor instead. -func (*SegData) Descriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{10} +func (m *SegData) XXX_Merge(src proto.Message) { + xxx_messageInfo_SegData.Merge(m, src) +} +func (m *SegData) XXX_Size() int { + return xxx_messageInfo_SegData.Size(m) +} +func (m *SegData) XXX_DiscardUnknown() { + xxx_messageInfo_SegData.DiscardUnknown(m) } -func (x *SegData) GetManifestId() []byte { - if x != nil { - return x.ManifestId +var xxx_messageInfo_SegData proto.InternalMessageInfo + +func (m *SegData) GetManifestId() []byte { + if m != nil { + return m.ManifestId } return nil } -func (x *SegData) GetSeq() int64 { - if x != nil { - return x.Seq +func (m *SegData) GetSeq() int64 { + if m != nil { + return m.Seq } return 0 } -func (x *SegData) GetHash() []byte { - if x != nil { - return x.Hash +func (m *SegData) GetHash() []byte { + if m != nil { + return m.Hash } return nil } -func (x *SegData) GetProfiles() []byte { - if x != nil { - return x.Profiles +func (m *SegData) GetProfiles() []byte { + if m != nil { + return m.Profiles } return nil } -func (x *SegData) GetSig() []byte { - if x != nil { - return x.Sig +func (m *SegData) GetSig() []byte { + if m != nil { + return m.Sig } return nil } -func (x *SegData) GetDuration() int32 { - if x != nil { - return x.Duration +func (m *SegData) GetDuration() int32 { + if m != nil { + return m.Duration } return 0 } -func (x *SegData) GetCapabilities() *Capabilities { - if x != nil { - return x.Capabilities +func (m *SegData) GetCapabilities() *Capabilities { + if m != nil { + return m.Capabilities } return nil } -func (x *SegData) GetAuthToken() *AuthToken { - if x != nil { - return x.AuthToken +func (m *SegData) GetAuthToken() *AuthToken { + if m != nil { + return m.AuthToken } return nil } -func (x *SegData) GetCalcPerceptualHash() bool { - if x != nil { - return x.CalcPerceptualHash +func (m *SegData) GetCalcPerceptualHash() bool { + if m != nil { + return m.CalcPerceptualHash } return false } -func (x *SegData) GetStorage() []*OSInfo { - if x != nil { - return x.Storage +func (m *SegData) GetStorage() []*OSInfo { + if m != nil { + return m.Storage } return nil } -func (x *SegData) GetFullProfiles() []*VideoProfile { - if x != nil { - return x.FullProfiles +func (m *SegData) GetFullProfiles() []*VideoProfile { + if m != nil { + return m.FullProfiles } return nil } -func (x *SegData) GetFullProfiles2() []*VideoProfile { - if x != nil { - return x.FullProfiles2 +func (m *SegData) GetFullProfiles2() []*VideoProfile { + if m != nil { + return m.FullProfiles2 } return nil } -func (x *SegData) GetFullProfiles3() []*VideoProfile { - if x != nil { - return x.FullProfiles3 +func (m *SegData) GetFullProfiles3() []*VideoProfile { + if m != nil { + return m.FullProfiles3 } return nil } -func (x *SegData) GetSegmentParameters() *SegParameters { - if x != nil { - return x.SegmentParameters +func (m *SegData) GetSegmentParameters() *SegParameters { + if m != nil { + return m.SegmentParameters } return nil } -func (x *SegData) GetForceSessionReinit() bool { - if x != nil { - return x.ForceSessionReinit +func (m *SegData) GetForceSessionReinit() bool { + if m != nil { + return m.ForceSessionReinit } return false } type SegParameters struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - // Start timestamp from which to start encoding // Milliseconds, from start of the file From uint64 `protobuf:"varint,1,opt,name=from,proto3" json:"from,omitempty"` // Skip all frames after that timestamp // Milliseconds, from start of the file - To uint64 `protobuf:"varint,2,opt,name=to,proto3" json:"to,omitempty"` + To uint64 `protobuf:"varint,2,opt,name=to,proto3" json:"to,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *SegParameters) Reset() { - *x = SegParameters{} - if protoimpl.UnsafeEnabled { - mi := &file_net_lp_rpc_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *SegParameters) Reset() { *m = SegParameters{} } +func (m *SegParameters) String() string { return proto.CompactTextString(m) } +func (*SegParameters) ProtoMessage() {} +func (*SegParameters) Descriptor() ([]byte, []int) { + return fileDescriptor_034e29c79f9ba827, []int{11} } -func (x *SegParameters) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *SegParameters) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SegParameters.Unmarshal(m, b) } - -func (*SegParameters) ProtoMessage() {} - -func (x *SegParameters) ProtoReflect() protoreflect.Message { - mi := &file_net_lp_rpc_proto_msgTypes[11] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) +func (m *SegParameters) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SegParameters.Marshal(b, m, deterministic) } - -// Deprecated: Use SegParameters.ProtoReflect.Descriptor instead. -func (*SegParameters) Descriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{11} +func (m *SegParameters) XXX_Merge(src proto.Message) { + xxx_messageInfo_SegParameters.Merge(m, src) +} +func (m *SegParameters) XXX_Size() int { + return xxx_messageInfo_SegParameters.Size(m) +} +func (m *SegParameters) XXX_DiscardUnknown() { + xxx_messageInfo_SegParameters.DiscardUnknown(m) } -func (x *SegParameters) GetFrom() uint64 { - if x != nil { - return x.From +var xxx_messageInfo_SegParameters proto.InternalMessageInfo + +func (m *SegParameters) GetFrom() uint64 { + if m != nil { + return m.From } return 0 } -func (x *SegParameters) GetTo() uint64 { - if x != nil { - return x.To +func (m *SegParameters) GetTo() uint64 { + if m != nil { + return m.To } return 0 } type VideoProfile struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - // Name of VideoProfile Name string `protobuf:"bytes,16,opt,name=name,proto3" json:"name,omitempty"` // Width of VideoProfile @@ -1192,318 +1127,306 @@ type VideoProfile struct { // GOP interval Gop int32 `protobuf:"varint,24,opt,name=gop,proto3" json:"gop,omitempty"` // Encoder (video codec) - Encoder VideoProfile_VideoCodec `protobuf:"varint,25,opt,name=encoder,proto3,enum=net.VideoProfile_VideoCodec" json:"encoder,omitempty"` - ColorDepth int32 `protobuf:"varint,26,opt,name=colorDepth,proto3" json:"colorDepth,omitempty"` - ChromaFormat VideoProfile_ChromaSubsampling `protobuf:"varint,27,opt,name=chromaFormat,proto3,enum=net.VideoProfile_ChromaSubsampling" json:"chromaFormat,omitempty"` - Quality uint32 `protobuf:"varint,28,opt,name=quality,proto3" json:"quality,omitempty"` + Encoder VideoProfile_VideoCodec `protobuf:"varint,25,opt,name=encoder,proto3,enum=net.VideoProfile_VideoCodec" json:"encoder,omitempty"` + ColorDepth int32 `protobuf:"varint,26,opt,name=colorDepth,proto3" json:"colorDepth,omitempty"` + ChromaFormat VideoProfile_ChromaSubsampling `protobuf:"varint,27,opt,name=chromaFormat,proto3,enum=net.VideoProfile_ChromaSubsampling" json:"chromaFormat,omitempty"` + Quality uint32 `protobuf:"varint,28,opt,name=quality,proto3" json:"quality,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *VideoProfile) Reset() { *m = VideoProfile{} } +func (m *VideoProfile) String() string { return proto.CompactTextString(m) } +func (*VideoProfile) ProtoMessage() {} +func (*VideoProfile) Descriptor() ([]byte, []int) { + return fileDescriptor_034e29c79f9ba827, []int{12} } -func (x *VideoProfile) Reset() { - *x = VideoProfile{} - if protoimpl.UnsafeEnabled { - mi := &file_net_lp_rpc_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *VideoProfile) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_VideoProfile.Unmarshal(m, b) } - -func (x *VideoProfile) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *VideoProfile) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_VideoProfile.Marshal(b, m, deterministic) } - -func (*VideoProfile) ProtoMessage() {} - -func (x *VideoProfile) ProtoReflect() protoreflect.Message { - mi := &file_net_lp_rpc_proto_msgTypes[12] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) +func (m *VideoProfile) XXX_Merge(src proto.Message) { + xxx_messageInfo_VideoProfile.Merge(m, src) } - -// Deprecated: Use VideoProfile.ProtoReflect.Descriptor instead. -func (*VideoProfile) Descriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{12} +func (m *VideoProfile) XXX_Size() int { + return xxx_messageInfo_VideoProfile.Size(m) } +func (m *VideoProfile) XXX_DiscardUnknown() { + xxx_messageInfo_VideoProfile.DiscardUnknown(m) +} + +var xxx_messageInfo_VideoProfile proto.InternalMessageInfo -func (x *VideoProfile) GetName() string { - if x != nil { - return x.Name +func (m *VideoProfile) GetName() string { + if m != nil { + return m.Name } return "" } -func (x *VideoProfile) GetWidth() int32 { - if x != nil { - return x.Width +func (m *VideoProfile) GetWidth() int32 { + if m != nil { + return m.Width } return 0 } -func (x *VideoProfile) GetHeight() int32 { - if x != nil { - return x.Height +func (m *VideoProfile) GetHeight() int32 { + if m != nil { + return m.Height } return 0 } -func (x *VideoProfile) GetBitrate() int32 { - if x != nil { - return x.Bitrate +func (m *VideoProfile) GetBitrate() int32 { + if m != nil { + return m.Bitrate } return 0 } -func (x *VideoProfile) GetFps() uint32 { - if x != nil { - return x.Fps +func (m *VideoProfile) GetFps() uint32 { + if m != nil { + return m.Fps } return 0 } -func (x *VideoProfile) GetFormat() VideoProfile_Format { - if x != nil { - return x.Format +func (m *VideoProfile) GetFormat() VideoProfile_Format { + if m != nil { + return m.Format } return VideoProfile_MPEGTS } -func (x *VideoProfile) GetFpsDen() uint32 { - if x != nil { - return x.FpsDen +func (m *VideoProfile) GetFpsDen() uint32 { + if m != nil { + return m.FpsDen } return 0 } -func (x *VideoProfile) GetProfile() VideoProfile_Profile { - if x != nil { - return x.Profile +func (m *VideoProfile) GetProfile() VideoProfile_Profile { + if m != nil { + return m.Profile } return VideoProfile_ENCODER_DEFAULT } -func (x *VideoProfile) GetGop() int32 { - if x != nil { - return x.Gop +func (m *VideoProfile) GetGop() int32 { + if m != nil { + return m.Gop } return 0 } -func (x *VideoProfile) GetEncoder() VideoProfile_VideoCodec { - if x != nil { - return x.Encoder +func (m *VideoProfile) GetEncoder() VideoProfile_VideoCodec { + if m != nil { + return m.Encoder } return VideoProfile_H264 } -func (x *VideoProfile) GetColorDepth() int32 { - if x != nil { - return x.ColorDepth +func (m *VideoProfile) GetColorDepth() int32 { + if m != nil { + return m.ColorDepth } return 0 } -func (x *VideoProfile) GetChromaFormat() VideoProfile_ChromaSubsampling { - if x != nil { - return x.ChromaFormat +func (m *VideoProfile) GetChromaFormat() VideoProfile_ChromaSubsampling { + if m != nil { + return m.ChromaFormat } return VideoProfile_CHROMA_420 } -func (x *VideoProfile) GetQuality() uint32 { - if x != nil { - return x.Quality +func (m *VideoProfile) GetQuality() uint32 { + if m != nil { + return m.Quality } return 0 } // Individual transcoded segment data. type TranscodedSegmentData struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - // URL where the transcoded data can be downloaded from. Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` // Amount of pixels processed (output pixels) Pixels int64 `protobuf:"varint,2,opt,name=pixels,proto3" json:"pixels,omitempty"` // URL where the perceptual hash data can be downloaded from (can be empty) - PerceptualHashUrl string `protobuf:"bytes,3,opt,name=perceptual_hash_url,json=perceptualHashUrl,proto3" json:"perceptual_hash_url,omitempty"` + PerceptualHashUrl string `protobuf:"bytes,3,opt,name=perceptual_hash_url,json=perceptualHashUrl,proto3" json:"perceptual_hash_url,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *TranscodedSegmentData) Reset() { - *x = TranscodedSegmentData{} - if protoimpl.UnsafeEnabled { - mi := &file_net_lp_rpc_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *TranscodedSegmentData) Reset() { *m = TranscodedSegmentData{} } +func (m *TranscodedSegmentData) String() string { return proto.CompactTextString(m) } +func (*TranscodedSegmentData) ProtoMessage() {} +func (*TranscodedSegmentData) Descriptor() ([]byte, []int) { + return fileDescriptor_034e29c79f9ba827, []int{13} } -func (x *TranscodedSegmentData) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *TranscodedSegmentData) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_TranscodedSegmentData.Unmarshal(m, b) } - -func (*TranscodedSegmentData) ProtoMessage() {} - -func (x *TranscodedSegmentData) ProtoReflect() protoreflect.Message { - mi := &file_net_lp_rpc_proto_msgTypes[13] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) +func (m *TranscodedSegmentData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_TranscodedSegmentData.Marshal(b, m, deterministic) } - -// Deprecated: Use TranscodedSegmentData.ProtoReflect.Descriptor instead. -func (*TranscodedSegmentData) Descriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{13} +func (m *TranscodedSegmentData) XXX_Merge(src proto.Message) { + xxx_messageInfo_TranscodedSegmentData.Merge(m, src) +} +func (m *TranscodedSegmentData) XXX_Size() int { + return xxx_messageInfo_TranscodedSegmentData.Size(m) } +func (m *TranscodedSegmentData) XXX_DiscardUnknown() { + xxx_messageInfo_TranscodedSegmentData.DiscardUnknown(m) +} + +var xxx_messageInfo_TranscodedSegmentData proto.InternalMessageInfo -func (x *TranscodedSegmentData) GetUrl() string { - if x != nil { - return x.Url +func (m *TranscodedSegmentData) GetUrl() string { + if m != nil { + return m.Url } return "" } -func (x *TranscodedSegmentData) GetPixels() int64 { - if x != nil { - return x.Pixels +func (m *TranscodedSegmentData) GetPixels() int64 { + if m != nil { + return m.Pixels } return 0 } -func (x *TranscodedSegmentData) GetPerceptualHashUrl() string { - if x != nil { - return x.PerceptualHashUrl +func (m *TranscodedSegmentData) GetPerceptualHashUrl() string { + if m != nil { + return m.PerceptualHashUrl } return "" } // A set of transcoded segments following the profiles specified in the job. type TranscodeData struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - // Transcoded data, in the order specified in the job options Segments []*TranscodedSegmentData `protobuf:"bytes,1,rep,name=segments,proto3" json:"segments,omitempty"` // Signature of the hash of the concatenated hashes - Sig []byte `protobuf:"bytes,2,opt,name=sig,proto3" json:"sig,omitempty"` + Sig []byte `protobuf:"bytes,2,opt,name=sig,proto3" json:"sig,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *TranscodeData) Reset() { - *x = TranscodeData{} - if protoimpl.UnsafeEnabled { - mi := &file_net_lp_rpc_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *TranscodeData) Reset() { *m = TranscodeData{} } +func (m *TranscodeData) String() string { return proto.CompactTextString(m) } +func (*TranscodeData) ProtoMessage() {} +func (*TranscodeData) Descriptor() ([]byte, []int) { + return fileDescriptor_034e29c79f9ba827, []int{14} } -func (x *TranscodeData) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *TranscodeData) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_TranscodeData.Unmarshal(m, b) } - -func (*TranscodeData) ProtoMessage() {} - -func (x *TranscodeData) ProtoReflect() protoreflect.Message { - mi := &file_net_lp_rpc_proto_msgTypes[14] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) +func (m *TranscodeData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_TranscodeData.Marshal(b, m, deterministic) } - -// Deprecated: Use TranscodeData.ProtoReflect.Descriptor instead. -func (*TranscodeData) Descriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{14} +func (m *TranscodeData) XXX_Merge(src proto.Message) { + xxx_messageInfo_TranscodeData.Merge(m, src) +} +func (m *TranscodeData) XXX_Size() int { + return xxx_messageInfo_TranscodeData.Size(m) +} +func (m *TranscodeData) XXX_DiscardUnknown() { + xxx_messageInfo_TranscodeData.DiscardUnknown(m) } -func (x *TranscodeData) GetSegments() []*TranscodedSegmentData { - if x != nil { - return x.Segments +var xxx_messageInfo_TranscodeData proto.InternalMessageInfo + +func (m *TranscodeData) GetSegments() []*TranscodedSegmentData { + if m != nil { + return m.Segments } return nil } -func (x *TranscodeData) GetSig() []byte { - if x != nil { - return x.Sig +func (m *TranscodeData) GetSig() []byte { + if m != nil { + return m.Sig } return nil } // Response that a transcoder sends after transcoding a segment. type TranscodeResult struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - // Sequence number of the transcoded results. Seq int64 `protobuf:"varint,1,opt,name=seq,proto3" json:"seq,omitempty"` // Result of transcoding can be an error, or successful with more info // - // Types that are assignable to Result: + // Types that are valid to be assigned to Result: // // *TranscodeResult_Error // *TranscodeResult_Data Result isTranscodeResult_Result `protobuf_oneof:"result"` // Used to notify a broadcaster of updated orchestrator information - Info *OrchestratorInfo `protobuf:"bytes,16,opt,name=info,proto3" json:"info,omitempty"` + Info *OrchestratorInfo `protobuf:"bytes,16,opt,name=info,proto3" json:"info,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *TranscodeResult) Reset() { - *x = TranscodeResult{} - if protoimpl.UnsafeEnabled { - mi := &file_net_lp_rpc_proto_msgTypes[15] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *TranscodeResult) Reset() { *m = TranscodeResult{} } +func (m *TranscodeResult) String() string { return proto.CompactTextString(m) } +func (*TranscodeResult) ProtoMessage() {} +func (*TranscodeResult) Descriptor() ([]byte, []int) { + return fileDescriptor_034e29c79f9ba827, []int{15} } -func (x *TranscodeResult) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *TranscodeResult) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_TranscodeResult.Unmarshal(m, b) +} +func (m *TranscodeResult) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_TranscodeResult.Marshal(b, m, deterministic) +} +func (m *TranscodeResult) XXX_Merge(src proto.Message) { + xxx_messageInfo_TranscodeResult.Merge(m, src) +} +func (m *TranscodeResult) XXX_Size() int { + return xxx_messageInfo_TranscodeResult.Size(m) +} +func (m *TranscodeResult) XXX_DiscardUnknown() { + xxx_messageInfo_TranscodeResult.DiscardUnknown(m) } -func (*TranscodeResult) ProtoMessage() {} +var xxx_messageInfo_TranscodeResult proto.InternalMessageInfo -func (x *TranscodeResult) ProtoReflect() protoreflect.Message { - mi := &file_net_lp_rpc_proto_msgTypes[15] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (m *TranscodeResult) GetSeq() int64 { + if m != nil { + return m.Seq } - return mi.MessageOf(x) + return 0 } -// Deprecated: Use TranscodeResult.ProtoReflect.Descriptor instead. -func (*TranscodeResult) Descriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{15} +type isTranscodeResult_Result interface { + isTranscodeResult_Result() } -func (x *TranscodeResult) GetSeq() int64 { - if x != nil { - return x.Seq - } - return 0 +type TranscodeResult_Error struct { + Error string `protobuf:"bytes,2,opt,name=error,proto3,oneof"` +} + +type TranscodeResult_Data struct { + Data *TranscodeData `protobuf:"bytes,3,opt,name=data,proto3,oneof"` } +func (*TranscodeResult_Error) isTranscodeResult_Result() {} + +func (*TranscodeResult_Data) isTranscodeResult_Result() {} + func (m *TranscodeResult) GetResult() isTranscodeResult_Result { if m != nil { return m.Result @@ -1511,116 +1434,96 @@ func (m *TranscodeResult) GetResult() isTranscodeResult_Result { return nil } -func (x *TranscodeResult) GetError() string { - if x, ok := x.GetResult().(*TranscodeResult_Error); ok { +func (m *TranscodeResult) GetError() string { + if x, ok := m.GetResult().(*TranscodeResult_Error); ok { return x.Error } return "" } -func (x *TranscodeResult) GetData() *TranscodeData { - if x, ok := x.GetResult().(*TranscodeResult_Data); ok { +func (m *TranscodeResult) GetData() *TranscodeData { + if x, ok := m.GetResult().(*TranscodeResult_Data); ok { return x.Data } return nil } -func (x *TranscodeResult) GetInfo() *OrchestratorInfo { - if x != nil { - return x.Info +func (m *TranscodeResult) GetInfo() *OrchestratorInfo { + if m != nil { + return m.Info } return nil } -type isTranscodeResult_Result interface { - isTranscodeResult_Result() -} - -type TranscodeResult_Error struct { - Error string `protobuf:"bytes,2,opt,name=error,proto3,oneof"` -} - -type TranscodeResult_Data struct { - Data *TranscodeData `protobuf:"bytes,3,opt,name=data,proto3,oneof"` +// XXX_OneofWrappers is for the internal use of the proto package. +func (*TranscodeResult) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*TranscodeResult_Error)(nil), + (*TranscodeResult_Data)(nil), + } } -func (*TranscodeResult_Error) isTranscodeResult_Result() {} - -func (*TranscodeResult_Data) isTranscodeResult_Result() {} - // Sent by the transcoder to register itself to the orchestrator. type RegisterRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - // Shared secret for auth Secret string `protobuf:"bytes,1,opt,name=secret,proto3" json:"secret,omitempty"` // Transcoder capacity Capacity int64 `protobuf:"varint,2,opt,name=capacity,proto3" json:"capacity,omitempty"` // Transcoder capabilities - Capabilities *Capabilities `protobuf:"bytes,3,opt,name=capabilities,proto3" json:"capabilities,omitempty"` + Capabilities *Capabilities `protobuf:"bytes,3,opt,name=capabilities,proto3" json:"capabilities,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *RegisterRequest) Reset() { - *x = RegisterRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_net_lp_rpc_proto_msgTypes[16] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *RegisterRequest) Reset() { *m = RegisterRequest{} } +func (m *RegisterRequest) String() string { return proto.CompactTextString(m) } +func (*RegisterRequest) ProtoMessage() {} +func (*RegisterRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_034e29c79f9ba827, []int{16} } -func (x *RegisterRequest) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *RegisterRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RegisterRequest.Unmarshal(m, b) } - -func (*RegisterRequest) ProtoMessage() {} - -func (x *RegisterRequest) ProtoReflect() protoreflect.Message { - mi := &file_net_lp_rpc_proto_msgTypes[16] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) +func (m *RegisterRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RegisterRequest.Marshal(b, m, deterministic) } - -// Deprecated: Use RegisterRequest.ProtoReflect.Descriptor instead. -func (*RegisterRequest) Descriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{16} +func (m *RegisterRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RegisterRequest.Merge(m, src) +} +func (m *RegisterRequest) XXX_Size() int { + return xxx_messageInfo_RegisterRequest.Size(m) } +func (m *RegisterRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RegisterRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_RegisterRequest proto.InternalMessageInfo -func (x *RegisterRequest) GetSecret() string { - if x != nil { - return x.Secret +func (m *RegisterRequest) GetSecret() string { + if m != nil { + return m.Secret } return "" } -func (x *RegisterRequest) GetCapacity() int64 { - if x != nil { - return x.Capacity +func (m *RegisterRequest) GetCapacity() int64 { + if m != nil { + return m.Capacity } return 0 } -func (x *RegisterRequest) GetCapabilities() *Capabilities { - if x != nil { - return x.Capabilities +func (m *RegisterRequest) GetCapabilities() *Capabilities { + if m != nil { + return m.Capabilities } return nil } // Sent by the orchestrator to the transcoder type NotifySegment struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - // URL of the segment to transcode. Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` // Configuration for the transcoding job @@ -1629,75 +1532,67 @@ type NotifySegment struct { TaskId int64 `protobuf:"varint,16,opt,name=taskId,proto3" json:"taskId,omitempty"` // Deprecated by fullProfiles. Set of presets to transcode into. // Should be set to an invalid value to induce failures - Profiles []byte `protobuf:"bytes,17,opt,name=profiles,proto3" json:"profiles,omitempty"` + Profiles []byte `protobuf:"bytes,17,opt,name=profiles,proto3" json:"profiles,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *NotifySegment) Reset() { - *x = NotifySegment{} - if protoimpl.UnsafeEnabled { - mi := &file_net_lp_rpc_proto_msgTypes[17] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *NotifySegment) Reset() { *m = NotifySegment{} } +func (m *NotifySegment) String() string { return proto.CompactTextString(m) } +func (*NotifySegment) ProtoMessage() {} +func (*NotifySegment) Descriptor() ([]byte, []int) { + return fileDescriptor_034e29c79f9ba827, []int{17} } -func (x *NotifySegment) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *NotifySegment) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_NotifySegment.Unmarshal(m, b) } - -func (*NotifySegment) ProtoMessage() {} - -func (x *NotifySegment) ProtoReflect() protoreflect.Message { - mi := &file_net_lp_rpc_proto_msgTypes[17] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) +func (m *NotifySegment) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_NotifySegment.Marshal(b, m, deterministic) } - -// Deprecated: Use NotifySegment.ProtoReflect.Descriptor instead. -func (*NotifySegment) Descriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{17} +func (m *NotifySegment) XXX_Merge(src proto.Message) { + xxx_messageInfo_NotifySegment.Merge(m, src) +} +func (m *NotifySegment) XXX_Size() int { + return xxx_messageInfo_NotifySegment.Size(m) } +func (m *NotifySegment) XXX_DiscardUnknown() { + xxx_messageInfo_NotifySegment.DiscardUnknown(m) +} + +var xxx_messageInfo_NotifySegment proto.InternalMessageInfo -func (x *NotifySegment) GetUrl() string { - if x != nil { - return x.Url +func (m *NotifySegment) GetUrl() string { + if m != nil { + return m.Url } return "" } -func (x *NotifySegment) GetSegData() *SegData { - if x != nil { - return x.SegData +func (m *NotifySegment) GetSegData() *SegData { + if m != nil { + return m.SegData } return nil } -func (x *NotifySegment) GetTaskId() int64 { - if x != nil { - return x.TaskId +func (m *NotifySegment) GetTaskId() int64 { + if m != nil { + return m.TaskId } return 0 } -func (x *NotifySegment) GetProfiles() []byte { - if x != nil { - return x.Profiles +func (m *NotifySegment) GetProfiles() []byte { + if m != nil { + return m.Profiles } return nil } // Required parameters for probabilistic micropayment tickets type TicketParams struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - // ETH address of the recipient Recipient []byte `protobuf:"bytes,1,opt,name=recipient,proto3" json:"recipient,omitempty"` // Pay out (in Wei) to the recipient if the ticket wins @@ -1713,203 +1608,183 @@ type TicketParams struct { // Block number at which the current set of advertised TicketParams is no longer valid ExpirationBlock []byte `protobuf:"bytes,6,opt,name=expiration_block,json=expirationBlock,proto3" json:"expiration_block,omitempty"` // Expected ticket expiration params - ExpirationParams *TicketExpirationParams `protobuf:"bytes,7,opt,name=expiration_params,json=expirationParams,proto3" json:"expiration_params,omitempty"` + ExpirationParams *TicketExpirationParams `protobuf:"bytes,7,opt,name=expiration_params,json=expirationParams,proto3" json:"expiration_params,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *TicketParams) Reset() { - *x = TicketParams{} - if protoimpl.UnsafeEnabled { - mi := &file_net_lp_rpc_proto_msgTypes[18] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *TicketParams) Reset() { *m = TicketParams{} } +func (m *TicketParams) String() string { return proto.CompactTextString(m) } +func (*TicketParams) ProtoMessage() {} +func (*TicketParams) Descriptor() ([]byte, []int) { + return fileDescriptor_034e29c79f9ba827, []int{18} } -func (x *TicketParams) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *TicketParams) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_TicketParams.Unmarshal(m, b) } - -func (*TicketParams) ProtoMessage() {} - -func (x *TicketParams) ProtoReflect() protoreflect.Message { - mi := &file_net_lp_rpc_proto_msgTypes[18] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) +func (m *TicketParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_TicketParams.Marshal(b, m, deterministic) } - -// Deprecated: Use TicketParams.ProtoReflect.Descriptor instead. -func (*TicketParams) Descriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{18} +func (m *TicketParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_TicketParams.Merge(m, src) +} +func (m *TicketParams) XXX_Size() int { + return xxx_messageInfo_TicketParams.Size(m) +} +func (m *TicketParams) XXX_DiscardUnknown() { + xxx_messageInfo_TicketParams.DiscardUnknown(m) } -func (x *TicketParams) GetRecipient() []byte { - if x != nil { - return x.Recipient +var xxx_messageInfo_TicketParams proto.InternalMessageInfo + +func (m *TicketParams) GetRecipient() []byte { + if m != nil { + return m.Recipient } return nil } -func (x *TicketParams) GetFaceValue() []byte { - if x != nil { - return x.FaceValue +func (m *TicketParams) GetFaceValue() []byte { + if m != nil { + return m.FaceValue } return nil } -func (x *TicketParams) GetWinProb() []byte { - if x != nil { - return x.WinProb +func (m *TicketParams) GetWinProb() []byte { + if m != nil { + return m.WinProb } return nil } -func (x *TicketParams) GetRecipientRandHash() []byte { - if x != nil { - return x.RecipientRandHash +func (m *TicketParams) GetRecipientRandHash() []byte { + if m != nil { + return m.RecipientRandHash } return nil } -func (x *TicketParams) GetSeed() []byte { - if x != nil { - return x.Seed +func (m *TicketParams) GetSeed() []byte { + if m != nil { + return m.Seed } return nil } -func (x *TicketParams) GetExpirationBlock() []byte { - if x != nil { - return x.ExpirationBlock +func (m *TicketParams) GetExpirationBlock() []byte { + if m != nil { + return m.ExpirationBlock } return nil } -func (x *TicketParams) GetExpirationParams() *TicketExpirationParams { - if x != nil { - return x.ExpirationParams +func (m *TicketParams) GetExpirationParams() *TicketExpirationParams { + if m != nil { + return m.ExpirationParams } return nil } // Sender Params (nonces and signatures) type TicketSenderParams struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - // Monotonically increasing counter that makes the ticket // unique relative to a particular hash commitment to a recipient's random number SenderNonce uint32 `protobuf:"varint,1,opt,name=sender_nonce,json=senderNonce,proto3" json:"sender_nonce,omitempty"` // Sender signature over the ticket - Sig []byte `protobuf:"bytes,2,opt,name=sig,proto3" json:"sig,omitempty"` + Sig []byte `protobuf:"bytes,2,opt,name=sig,proto3" json:"sig,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *TicketSenderParams) Reset() { - *x = TicketSenderParams{} - if protoimpl.UnsafeEnabled { - mi := &file_net_lp_rpc_proto_msgTypes[19] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *TicketSenderParams) Reset() { *m = TicketSenderParams{} } +func (m *TicketSenderParams) String() string { return proto.CompactTextString(m) } +func (*TicketSenderParams) ProtoMessage() {} +func (*TicketSenderParams) Descriptor() ([]byte, []int) { + return fileDescriptor_034e29c79f9ba827, []int{19} } -func (x *TicketSenderParams) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *TicketSenderParams) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_TicketSenderParams.Unmarshal(m, b) } - -func (*TicketSenderParams) ProtoMessage() {} - -func (x *TicketSenderParams) ProtoReflect() protoreflect.Message { - mi := &file_net_lp_rpc_proto_msgTypes[19] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) +func (m *TicketSenderParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_TicketSenderParams.Marshal(b, m, deterministic) } - -// Deprecated: Use TicketSenderParams.ProtoReflect.Descriptor instead. -func (*TicketSenderParams) Descriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{19} +func (m *TicketSenderParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_TicketSenderParams.Merge(m, src) +} +func (m *TicketSenderParams) XXX_Size() int { + return xxx_messageInfo_TicketSenderParams.Size(m) } +func (m *TicketSenderParams) XXX_DiscardUnknown() { + xxx_messageInfo_TicketSenderParams.DiscardUnknown(m) +} + +var xxx_messageInfo_TicketSenderParams proto.InternalMessageInfo -func (x *TicketSenderParams) GetSenderNonce() uint32 { - if x != nil { - return x.SenderNonce +func (m *TicketSenderParams) GetSenderNonce() uint32 { + if m != nil { + return m.SenderNonce } return 0 } -func (x *TicketSenderParams) GetSig() []byte { - if x != nil { - return x.Sig +func (m *TicketSenderParams) GetSig() []byte { + if m != nil { + return m.Sig } return nil } // Ticket params for expiration related validation type TicketExpirationParams struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - // Round during which tickets are created CreationRound int64 `protobuf:"varint,1,opt,name=creation_round,json=creationRound,proto3" json:"creation_round,omitempty"` // Block hash associated with creation_round - CreationRoundBlockHash []byte `protobuf:"bytes,2,opt,name=creation_round_block_hash,json=creationRoundBlockHash,proto3" json:"creation_round_block_hash,omitempty"` + CreationRoundBlockHash []byte `protobuf:"bytes,2,opt,name=creation_round_block_hash,json=creationRoundBlockHash,proto3" json:"creation_round_block_hash,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *TicketExpirationParams) Reset() { - *x = TicketExpirationParams{} - if protoimpl.UnsafeEnabled { - mi := &file_net_lp_rpc_proto_msgTypes[20] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *TicketExpirationParams) Reset() { *m = TicketExpirationParams{} } +func (m *TicketExpirationParams) String() string { return proto.CompactTextString(m) } +func (*TicketExpirationParams) ProtoMessage() {} +func (*TicketExpirationParams) Descriptor() ([]byte, []int) { + return fileDescriptor_034e29c79f9ba827, []int{20} } -func (x *TicketExpirationParams) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *TicketExpirationParams) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_TicketExpirationParams.Unmarshal(m, b) } - -func (*TicketExpirationParams) ProtoMessage() {} - -func (x *TicketExpirationParams) ProtoReflect() protoreflect.Message { - mi := &file_net_lp_rpc_proto_msgTypes[20] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) +func (m *TicketExpirationParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_TicketExpirationParams.Marshal(b, m, deterministic) } - -// Deprecated: Use TicketExpirationParams.ProtoReflect.Descriptor instead. -func (*TicketExpirationParams) Descriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{20} +func (m *TicketExpirationParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_TicketExpirationParams.Merge(m, src) +} +func (m *TicketExpirationParams) XXX_Size() int { + return xxx_messageInfo_TicketExpirationParams.Size(m) +} +func (m *TicketExpirationParams) XXX_DiscardUnknown() { + xxx_messageInfo_TicketExpirationParams.DiscardUnknown(m) } -func (x *TicketExpirationParams) GetCreationRound() int64 { - if x != nil { - return x.CreationRound +var xxx_messageInfo_TicketExpirationParams proto.InternalMessageInfo + +func (m *TicketExpirationParams) GetCreationRound() int64 { + if m != nil { + return m.CreationRound } return 0 } -func (x *TicketExpirationParams) GetCreationRoundBlockHash() []byte { - if x != nil { - return x.CreationRoundBlockHash +func (m *TicketExpirationParams) GetCreationRoundBlockHash() []byte { + if m != nil { + return m.CreationRoundBlockHash } return nil } @@ -1918,10 +1793,6 @@ func (x *TicketExpirationParams) GetCreationRoundBlockHash() []byte { // A payment can constitute of multiple tickets // A broadcaster might need to send multiple tickets to top up his credit with an Orchestrator type Payment struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - // Probabilistic micropayment ticket parameters // These remain the same even when sending multiple tickets TicketParams *TicketParams `protobuf:"bytes,1,opt,name=ticket_params,json=ticketParams,proto3" json:"ticket_params,omitempty"` @@ -1931,895 +1802,239 @@ type Payment struct { ExpirationParams *TicketExpirationParams `protobuf:"bytes,3,opt,name=expiration_params,json=expirationParams,proto3" json:"expiration_params,omitempty"` TicketSenderParams []*TicketSenderParams `protobuf:"bytes,4,rep,name=ticket_sender_params,json=ticketSenderParams,proto3" json:"ticket_sender_params,omitempty"` // O's last known price - ExpectedPrice *PriceInfo `protobuf:"bytes,5,opt,name=expected_price,json=expectedPrice,proto3" json:"expected_price,omitempty"` + ExpectedPrice *PriceInfo `protobuf:"bytes,5,opt,name=expected_price,json=expectedPrice,proto3" json:"expected_price,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *Payment) Reset() { - *x = Payment{} - if protoimpl.UnsafeEnabled { - mi := &file_net_lp_rpc_proto_msgTypes[21] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *Payment) Reset() { *m = Payment{} } +func (m *Payment) String() string { return proto.CompactTextString(m) } +func (*Payment) ProtoMessage() {} +func (*Payment) Descriptor() ([]byte, []int) { + return fileDescriptor_034e29c79f9ba827, []int{21} } -func (x *Payment) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *Payment) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Payment.Unmarshal(m, b) } - -func (*Payment) ProtoMessage() {} - -func (x *Payment) ProtoReflect() protoreflect.Message { - mi := &file_net_lp_rpc_proto_msgTypes[21] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) +func (m *Payment) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Payment.Marshal(b, m, deterministic) } - -// Deprecated: Use Payment.ProtoReflect.Descriptor instead. -func (*Payment) Descriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{21} +func (m *Payment) XXX_Merge(src proto.Message) { + xxx_messageInfo_Payment.Merge(m, src) } - -func (x *Payment) GetTicketParams() *TicketParams { - if x != nil { - return x.TicketParams - } - return nil +func (m *Payment) XXX_Size() int { + return xxx_messageInfo_Payment.Size(m) } - -func (x *Payment) GetSender() []byte { - if x != nil { - return x.Sender - } - return nil +func (m *Payment) XXX_DiscardUnknown() { + xxx_messageInfo_Payment.DiscardUnknown(m) } -func (x *Payment) GetExpirationParams() *TicketExpirationParams { - if x != nil { - return x.ExpirationParams - } - return nil -} +var xxx_messageInfo_Payment proto.InternalMessageInfo -func (x *Payment) GetTicketSenderParams() []*TicketSenderParams { - if x != nil { - return x.TicketSenderParams +func (m *Payment) GetTicketParams() *TicketParams { + if m != nil { + return m.TicketParams } return nil } -func (x *Payment) GetExpectedPrice() *PriceInfo { - if x != nil { - return x.ExpectedPrice +func (m *Payment) GetSender() []byte { + if m != nil { + return m.Sender } return nil } -// Non-binary capability constraints, such as supported ranges. -type Capabilities_Constraints struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Models map[string]*Capabilities_Constraints_ModelConstraint `protobuf:"bytes,1,rep,name=models,proto3" json:"models,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` -} - -func (x *Capabilities_Constraints) Reset() { - *x = Capabilities_Constraints{} - if protoimpl.UnsafeEnabled { - mi := &file_net_lp_rpc_proto_msgTypes[23] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Capabilities_Constraints) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Capabilities_Constraints) ProtoMessage() {} - -func (x *Capabilities_Constraints) ProtoReflect() protoreflect.Message { - mi := &file_net_lp_rpc_proto_msgTypes[23] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Capabilities_Constraints.ProtoReflect.Descriptor instead. -func (*Capabilities_Constraints) Descriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{7, 1} -} - -func (x *Capabilities_Constraints) GetModels() map[string]*Capabilities_Constraints_ModelConstraint { - if x != nil { - return x.Models +func (m *Payment) GetExpirationParams() *TicketExpirationParams { + if m != nil { + return m.ExpirationParams } return nil } -type Capabilities_Constraints_ModelConstraint struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Warm bool `protobuf:"varint,1,opt,name=warm,proto3" json:"warm,omitempty"` -} - -func (x *Capabilities_Constraints_ModelConstraint) Reset() { - *x = Capabilities_Constraints_ModelConstraint{} - if protoimpl.UnsafeEnabled { - mi := &file_net_lp_rpc_proto_msgTypes[25] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Capabilities_Constraints_ModelConstraint) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Capabilities_Constraints_ModelConstraint) ProtoMessage() {} - -func (x *Capabilities_Constraints_ModelConstraint) ProtoReflect() protoreflect.Message { - mi := &file_net_lp_rpc_proto_msgTypes[25] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (m *Payment) GetTicketSenderParams() []*TicketSenderParams { + if m != nil { + return m.TicketSenderParams } - return mi.MessageOf(x) -} - -// Deprecated: Use Capabilities_Constraints_ModelConstraint.ProtoReflect.Descriptor instead. -func (*Capabilities_Constraints_ModelConstraint) Descriptor() ([]byte, []int) { - return file_net_lp_rpc_proto_rawDescGZIP(), []int{7, 1, 0} + return nil } -func (x *Capabilities_Constraints_ModelConstraint) GetWarm() bool { - if x != nil { - return x.Warm +func (m *Payment) GetExpectedPrice() *PriceInfo { + if m != nil { + return m.ExpectedPrice } - return false + return nil } -var File_net_lp_rpc_proto protoreflect.FileDescriptor - -var file_net_lp_rpc_proto_rawDesc = []byte{ - 0x0a, 0x10, 0x6e, 0x65, 0x74, 0x2f, 0x6c, 0x70, 0x5f, 0x72, 0x70, 0x63, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x12, 0x03, 0x6e, 0x65, 0x74, 0x22, 0x20, 0x0a, 0x08, 0x50, 0x69, 0x6e, 0x67, 0x50, - 0x6f, 0x6e, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x4d, 0x0a, 0x1c, 0x45, 0x6e, 0x64, - 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x0a, 0x61, 0x75, 0x74, - 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, - 0x6e, 0x65, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x09, 0x61, - 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x1f, 0x0a, 0x1d, 0x45, 0x6e, 0x64, 0x54, - 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x78, 0x0a, 0x13, 0x4f, 0x72, 0x63, - 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, - 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x12, 0x35, 0x0a, 0x0c, - 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, - 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, - 0x69, 0x65, 0x73, 0x22, 0x99, 0x01, 0x0a, 0x06, 0x4f, 0x53, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x39, - 0x0a, 0x0b, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4f, 0x53, 0x49, 0x6e, 0x66, 0x6f, - 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x73, 0x74, - 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x25, 0x0a, 0x06, 0x73, 0x33, 0x69, - 0x6e, 0x66, 0x6f, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x6e, 0x65, 0x74, 0x2e, - 0x53, 0x33, 0x4f, 0x53, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x73, 0x33, 0x69, 0x6e, 0x66, 0x6f, - 0x22, 0x2d, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x0a, 0x0a, 0x06, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x10, 0x00, 0x12, 0x06, 0x0a, 0x02, 0x53, - 0x33, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x47, 0x4f, 0x4f, 0x47, 0x4c, 0x45, 0x10, 0x02, 0x22, - 0xa2, 0x01, 0x0a, 0x08, 0x53, 0x33, 0x4f, 0x53, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, - 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, - 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, - 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x72, - 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x78, 0x41, 0x6d, 0x7a, - 0x44, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x78, 0x41, 0x6d, 0x7a, - 0x44, 0x61, 0x74, 0x65, 0x22, 0x55, 0x0a, 0x09, 0x50, 0x72, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, - 0x6f, 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x72, 0x69, 0x63, 0x65, 0x50, 0x65, 0x72, 0x55, 0x6e, 0x69, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x70, 0x72, 0x69, 0x63, 0x65, 0x50, 0x65, - 0x72, 0x55, 0x6e, 0x69, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x50, - 0x65, 0x72, 0x55, 0x6e, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x70, 0x69, - 0x78, 0x65, 0x6c, 0x73, 0x50, 0x65, 0x72, 0x55, 0x6e, 0x69, 0x74, 0x22, 0xd9, 0x04, 0x0a, 0x0c, - 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, - 0x62, 0x69, 0x74, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x03, 0x28, 0x04, 0x52, - 0x09, 0x62, 0x69, 0x74, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x20, 0x0a, 0x0b, 0x6d, 0x61, - 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, - 0x0b, 0x6d, 0x61, 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x12, 0x41, 0x0a, 0x0a, - 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x21, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, - 0x69, 0x65, 0x73, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x0a, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, - 0x44, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x04, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, - 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, - 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, - 0x61, 0x69, 0x6e, 0x74, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, - 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x1a, 0xe1, 0x01, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, - 0x69, 0x6e, 0x74, 0x73, 0x12, 0x41, 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, - 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, - 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x1a, 0x25, 0x0a, 0x0f, 0x4d, 0x6f, 0x64, 0x65, 0x6c, - 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x77, 0x61, - 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x77, 0x61, 0x72, 0x6d, 0x1a, 0x68, - 0x0a, 0x0b, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x43, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, - 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, - 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x6f, - 0x64, 0x65, 0x6c, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5d, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x73, - 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x33, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, - 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, - 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc0, 0x02, 0x0a, 0x10, 0x4f, 0x72, 0x63, 0x68, - 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1e, 0x0a, 0x0a, - 0x74, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x36, 0x0a, 0x0d, - 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, - 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x0c, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x73, 0x12, 0x2d, 0x0a, 0x0a, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x6e, - 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, - 0x72, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x70, 0x72, 0x69, 0x63, 0x65, 0x49, - 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x35, 0x0a, - 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, - 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, - 0x74, 0x69, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, - 0x65, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x41, - 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x12, 0x25, 0x0a, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x20, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4f, 0x53, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x22, 0x60, 0x0a, 0x09, 0x41, 0x75, - 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1d, 0x0a, - 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, - 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xf4, 0x04, 0x0a, - 0x07, 0x53, 0x65, 0x67, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x6e, 0x69, - 0x66, 0x65, 0x73, 0x74, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6d, 0x61, - 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x65, 0x71, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x73, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, - 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1a, - 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, - 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, - 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, - 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x35, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61, - 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, - 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, - 0x73, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, - 0x2d, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x30, - 0x0a, 0x14, 0x63, 0x61, 0x6c, 0x63, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, - 0x6c, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x63, 0x61, - 0x6c, 0x63, 0x50, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x48, 0x61, 0x73, 0x68, - 0x12, 0x25, 0x0a, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x20, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x0b, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4f, 0x53, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, - 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x35, 0x0a, 0x0c, 0x66, 0x75, 0x6c, 0x6c, 0x50, - 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x21, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, - 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, - 0x52, 0x0c, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x37, - 0x0a, 0x0d, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x32, 0x18, - 0x22, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, 0x65, - 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x0d, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x72, - 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x32, 0x12, 0x37, 0x0a, 0x0d, 0x66, 0x75, 0x6c, 0x6c, 0x50, - 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x33, 0x18, 0x23, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, - 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, - 0x65, 0x52, 0x0d, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x33, - 0x12, 0x41, 0x0a, 0x12, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x72, 0x61, - 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x25, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6e, - 0x65, 0x74, 0x2e, 0x53, 0x65, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, - 0x52, 0x11, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, - 0x65, 0x72, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x26, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x12, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x69, - 0x6e, 0x69, 0x74, 0x22, 0x33, 0x0a, 0x0d, 0x53, 0x65, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, - 0x74, 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x74, 0x6f, 0x22, 0xcc, 0x05, 0x0a, 0x0c, 0x56, 0x69, 0x64, - 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, - 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x11, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x77, 0x69, - 0x64, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x12, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x62, - 0x69, 0x74, 0x72, 0x61, 0x74, 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x62, 0x69, - 0x74, 0x72, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x70, 0x73, 0x18, 0x14, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x03, 0x66, 0x70, 0x73, 0x12, 0x30, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, - 0x74, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, - 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x46, 0x6f, 0x72, 0x6d, 0x61, - 0x74, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x70, 0x73, - 0x44, 0x65, 0x6e, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x66, 0x70, 0x73, 0x44, 0x65, - 0x6e, 0x12, 0x33, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x17, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, - 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x07, 0x70, - 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x6f, 0x70, 0x18, 0x18, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x03, 0x67, 0x6f, 0x70, 0x12, 0x36, 0x0a, 0x07, 0x65, 0x6e, 0x63, 0x6f, - 0x64, 0x65, 0x72, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x6e, 0x65, 0x74, 0x2e, - 0x56, 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x56, 0x69, 0x64, - 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x52, 0x07, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, - 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x44, 0x65, 0x70, 0x74, 0x68, 0x18, 0x1a, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x44, 0x65, 0x70, 0x74, 0x68, - 0x12, 0x47, 0x0a, 0x0c, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, - 0x18, 0x1b, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, - 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x61, - 0x53, 0x75, 0x62, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x52, 0x0c, 0x63, 0x68, 0x72, - 0x6f, 0x6d, 0x61, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x71, 0x75, 0x61, - 0x6c, 0x69, 0x74, 0x79, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x71, 0x75, 0x61, 0x6c, - 0x69, 0x74, 0x79, 0x22, 0x1d, 0x0a, 0x06, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x0a, 0x0a, - 0x06, 0x4d, 0x50, 0x45, 0x47, 0x54, 0x53, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4d, 0x50, 0x34, - 0x10, 0x01, 0x22, 0x6a, 0x0a, 0x07, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x13, 0x0a, - 0x0f, 0x45, 0x4e, 0x43, 0x4f, 0x44, 0x45, 0x52, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, - 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x48, 0x32, 0x36, 0x34, 0x5f, 0x42, 0x41, 0x53, 0x45, 0x4c, - 0x49, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x48, 0x32, 0x36, 0x34, 0x5f, 0x4d, 0x41, - 0x49, 0x4e, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x48, 0x32, 0x36, 0x34, 0x5f, 0x48, 0x49, 0x47, - 0x48, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x48, 0x32, 0x36, 0x34, 0x5f, 0x43, 0x4f, 0x4e, 0x53, - 0x54, 0x52, 0x41, 0x49, 0x4e, 0x45, 0x44, 0x5f, 0x48, 0x49, 0x47, 0x48, 0x10, 0x04, 0x22, 0x32, - 0x0a, 0x0a, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x12, 0x08, 0x0a, 0x04, - 0x48, 0x32, 0x36, 0x34, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x32, 0x36, 0x35, 0x10, 0x01, - 0x12, 0x07, 0x0a, 0x03, 0x56, 0x50, 0x38, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x56, 0x50, 0x39, - 0x10, 0x03, 0x22, 0x43, 0x0a, 0x11, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x53, 0x75, 0x62, 0x73, - 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x48, 0x52, 0x4f, 0x4d, - 0x41, 0x5f, 0x34, 0x32, 0x30, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x48, 0x52, 0x4f, 0x4d, - 0x41, 0x5f, 0x34, 0x32, 0x32, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x48, 0x52, 0x4f, 0x4d, - 0x41, 0x5f, 0x34, 0x34, 0x34, 0x10, 0x02, 0x22, 0x71, 0x0a, 0x15, 0x54, 0x72, 0x61, 0x6e, 0x73, - 0x63, 0x6f, 0x64, 0x65, 0x64, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, - 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, - 0x72, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x06, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x70, 0x65, - 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x75, 0x72, - 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, - 0x75, 0x61, 0x6c, 0x48, 0x61, 0x73, 0x68, 0x55, 0x72, 0x6c, 0x22, 0x59, 0x0a, 0x0d, 0x54, 0x72, - 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x36, 0x0a, 0x08, 0x73, - 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x53, 0x65, - 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x73, 0x65, 0x67, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x03, 0x73, 0x69, 0x67, 0x22, 0x9a, 0x01, 0x0a, 0x0f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, - 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x65, 0x71, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x73, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x05, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x12, 0x28, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x12, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, - 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x29, 0x0a, - 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6e, 0x65, - 0x74, 0x2e, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x42, 0x08, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x22, 0x7c, 0x0a, 0x0f, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x35, 0x0a, 0x0c, 0x63, 0x61, 0x70, - 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, - 0x65, 0x73, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, - 0x22, 0x89, 0x01, 0x0a, 0x0d, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, 0x65, 0x67, 0x6d, 0x65, - 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x75, 0x72, 0x6c, 0x12, 0x26, 0x0a, 0x07, 0x73, 0x65, 0x67, 0x44, 0x61, 0x74, 0x61, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x65, 0x67, 0x44, - 0x61, 0x74, 0x61, 0x52, 0x07, 0x73, 0x65, 0x67, 0x44, 0x61, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x06, - 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x18, 0x10, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x74, 0x61, - 0x73, 0x6b, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, - 0x18, 0x11, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, - 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x21, 0x10, 0x22, 0x22, 0x9f, 0x02, 0x0a, - 0x0c, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x1c, 0x0a, - 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x66, - 0x61, 0x63, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x09, 0x66, 0x61, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x77, 0x69, - 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x62, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x77, 0x69, - 0x6e, 0x50, 0x72, 0x6f, 0x62, 0x12, 0x2e, 0x0a, 0x13, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, - 0x6e, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x64, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x11, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x6e, - 0x64, 0x48, 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x65, 0x64, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x04, 0x73, 0x65, 0x65, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x78, 0x70, - 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x48, 0x0a, 0x11, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1b, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x70, 0x69, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x10, 0x65, 0x78, - 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x49, - 0x0a, 0x12, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x6e, - 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x73, 0x65, 0x6e, 0x64, - 0x65, 0x72, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x22, 0x7a, 0x0a, 0x16, 0x54, 0x69, 0x63, - 0x6b, 0x65, 0x74, 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x63, 0x72, 0x65, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x39, 0x0a, 0x19, 0x63, 0x72, - 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x16, 0x63, - 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x48, 0x61, 0x73, 0x68, 0x22, 0xa5, 0x02, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x12, 0x36, 0x0a, 0x0d, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x70, 0x61, 0x72, 0x61, - 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, - 0x69, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x0c, 0x74, 0x69, 0x63, - 0x6b, 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, - 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, - 0x72, 0x12, 0x48, 0x0a, 0x11, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6e, - 0x65, 0x74, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x10, 0x65, 0x78, 0x70, 0x69, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x49, 0x0a, 0x14, 0x74, - 0x69, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x72, - 0x61, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6e, 0x65, 0x74, 0x2e, - 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, - 0x6d, 0x73, 0x52, 0x12, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, - 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x35, 0x0a, 0x0e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, - 0x65, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, - 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x72, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, - 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x72, 0x69, 0x63, 0x65, 0x32, 0xd8, 0x01, - 0x0a, 0x0c, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x42, - 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, - 0x72, 0x12, 0x18, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, - 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x6e, 0x65, - 0x74, 0x2e, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x5e, 0x0a, 0x15, 0x45, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, - 0x64, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x2e, 0x6e, 0x65, - 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, - 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, - 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, - 0x69, 0x6e, 0x67, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x0d, 0x2e, 0x6e, 0x65, 0x74, - 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6e, 0x67, 0x1a, 0x0d, 0x2e, 0x6e, 0x65, 0x74, 0x2e, - 0x50, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6e, 0x67, 0x32, 0x4e, 0x0a, 0x0a, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x40, 0x0a, 0x12, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x14, 0x2e, 0x6e, - 0x65, 0x74, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, - 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x42, 0x07, 0x5a, 0x05, 0x2e, 0x2f, 0x6e, 0x65, - 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_net_lp_rpc_proto_rawDescOnce sync.Once - file_net_lp_rpc_proto_rawDescData = file_net_lp_rpc_proto_rawDesc -) - -func file_net_lp_rpc_proto_rawDescGZIP() []byte { - file_net_lp_rpc_proto_rawDescOnce.Do(func() { - file_net_lp_rpc_proto_rawDescData = protoimpl.X.CompressGZIP(file_net_lp_rpc_proto_rawDescData) - }) - return file_net_lp_rpc_proto_rawDescData -} - -var file_net_lp_rpc_proto_enumTypes = make([]protoimpl.EnumInfo, 5) -var file_net_lp_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 27) -var file_net_lp_rpc_proto_goTypes = []interface{}{ - (OSInfo_StorageType)(0), // 0: net.OSInfo.StorageType - (VideoProfile_Format)(0), // 1: net.VideoProfile.Format - (VideoProfile_Profile)(0), // 2: net.VideoProfile.Profile - (VideoProfile_VideoCodec)(0), // 3: net.VideoProfile.VideoCodec - (VideoProfile_ChromaSubsampling)(0), // 4: net.VideoProfile.ChromaSubsampling - (*PingPong)(nil), // 5: net.PingPong - (*EndTranscodingSessionRequest)(nil), // 6: net.EndTranscodingSessionRequest - (*EndTranscodingSessionResponse)(nil), // 7: net.EndTranscodingSessionResponse - (*OrchestratorRequest)(nil), // 8: net.OrchestratorRequest - (*OSInfo)(nil), // 9: net.OSInfo - (*S3OSInfo)(nil), // 10: net.S3OSInfo - (*PriceInfo)(nil), // 11: net.PriceInfo - (*Capabilities)(nil), // 12: net.Capabilities - (*OrchestratorInfo)(nil), // 13: net.OrchestratorInfo - (*AuthToken)(nil), // 14: net.AuthToken - (*SegData)(nil), // 15: net.SegData - (*SegParameters)(nil), // 16: net.SegParameters - (*VideoProfile)(nil), // 17: net.VideoProfile - (*TranscodedSegmentData)(nil), // 18: net.TranscodedSegmentData - (*TranscodeData)(nil), // 19: net.TranscodeData - (*TranscodeResult)(nil), // 20: net.TranscodeResult - (*RegisterRequest)(nil), // 21: net.RegisterRequest - (*NotifySegment)(nil), // 22: net.NotifySegment - (*TicketParams)(nil), // 23: net.TicketParams - (*TicketSenderParams)(nil), // 24: net.TicketSenderParams - (*TicketExpirationParams)(nil), // 25: net.TicketExpirationParams - (*Payment)(nil), // 26: net.Payment - nil, // 27: net.Capabilities.CapacitiesEntry - (*Capabilities_Constraints)(nil), // 28: net.Capabilities.Constraints - nil, // 29: net.Capabilities.ConstraintsEntry - (*Capabilities_Constraints_ModelConstraint)(nil), // 30: net.Capabilities.Constraints.ModelConstraint - nil, // 31: net.Capabilities.Constraints.ModelsEntry -} -var file_net_lp_rpc_proto_depIdxs = []int32{ - 14, // 0: net.EndTranscodingSessionRequest.auth_token:type_name -> net.AuthToken - 12, // 1: net.OrchestratorRequest.capabilities:type_name -> net.Capabilities - 0, // 2: net.OSInfo.storageType:type_name -> net.OSInfo.StorageType - 10, // 3: net.OSInfo.s3info:type_name -> net.S3OSInfo - 27, // 4: net.Capabilities.capacities:type_name -> net.Capabilities.CapacitiesEntry - 29, // 5: net.Capabilities.constraints:type_name -> net.Capabilities.ConstraintsEntry - 23, // 6: net.OrchestratorInfo.ticket_params:type_name -> net.TicketParams - 11, // 7: net.OrchestratorInfo.price_info:type_name -> net.PriceInfo - 12, // 8: net.OrchestratorInfo.capabilities:type_name -> net.Capabilities - 14, // 9: net.OrchestratorInfo.auth_token:type_name -> net.AuthToken - 9, // 10: net.OrchestratorInfo.storage:type_name -> net.OSInfo - 12, // 11: net.SegData.capabilities:type_name -> net.Capabilities - 14, // 12: net.SegData.auth_token:type_name -> net.AuthToken - 9, // 13: net.SegData.storage:type_name -> net.OSInfo - 17, // 14: net.SegData.fullProfiles:type_name -> net.VideoProfile - 17, // 15: net.SegData.fullProfiles2:type_name -> net.VideoProfile - 17, // 16: net.SegData.fullProfiles3:type_name -> net.VideoProfile - 16, // 17: net.SegData.segment_parameters:type_name -> net.SegParameters - 1, // 18: net.VideoProfile.format:type_name -> net.VideoProfile.Format - 2, // 19: net.VideoProfile.profile:type_name -> net.VideoProfile.Profile - 3, // 20: net.VideoProfile.encoder:type_name -> net.VideoProfile.VideoCodec - 4, // 21: net.VideoProfile.chromaFormat:type_name -> net.VideoProfile.ChromaSubsampling - 18, // 22: net.TranscodeData.segments:type_name -> net.TranscodedSegmentData - 19, // 23: net.TranscodeResult.data:type_name -> net.TranscodeData - 13, // 24: net.TranscodeResult.info:type_name -> net.OrchestratorInfo - 12, // 25: net.RegisterRequest.capabilities:type_name -> net.Capabilities - 15, // 26: net.NotifySegment.segData:type_name -> net.SegData - 25, // 27: net.TicketParams.expiration_params:type_name -> net.TicketExpirationParams - 23, // 28: net.Payment.ticket_params:type_name -> net.TicketParams - 25, // 29: net.Payment.expiration_params:type_name -> net.TicketExpirationParams - 24, // 30: net.Payment.ticket_sender_params:type_name -> net.TicketSenderParams - 11, // 31: net.Payment.expected_price:type_name -> net.PriceInfo - 31, // 32: net.Capabilities.Constraints.models:type_name -> net.Capabilities.Constraints.ModelsEntry - 28, // 33: net.Capabilities.ConstraintsEntry.value:type_name -> net.Capabilities.Constraints - 30, // 34: net.Capabilities.Constraints.ModelsEntry.value:type_name -> net.Capabilities.Constraints.ModelConstraint - 8, // 35: net.Orchestrator.GetOrchestrator:input_type -> net.OrchestratorRequest - 6, // 36: net.Orchestrator.EndTranscodingSession:input_type -> net.EndTranscodingSessionRequest - 5, // 37: net.Orchestrator.Ping:input_type -> net.PingPong - 21, // 38: net.Transcoder.RegisterTranscoder:input_type -> net.RegisterRequest - 13, // 39: net.Orchestrator.GetOrchestrator:output_type -> net.OrchestratorInfo - 7, // 40: net.Orchestrator.EndTranscodingSession:output_type -> net.EndTranscodingSessionResponse - 5, // 41: net.Orchestrator.Ping:output_type -> net.PingPong - 22, // 42: net.Transcoder.RegisterTranscoder:output_type -> net.NotifySegment - 39, // [39:43] is the sub-list for method output_type - 35, // [35:39] is the sub-list for method input_type - 35, // [35:35] is the sub-list for extension type_name - 35, // [35:35] is the sub-list for extension extendee - 0, // [0:35] is the sub-list for field type_name -} - -func init() { file_net_lp_rpc_proto_init() } -func file_net_lp_rpc_proto_init() { - if File_net_lp_rpc_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_net_lp_rpc_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PingPong); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_net_lp_rpc_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EndTranscodingSessionRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_net_lp_rpc_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EndTranscodingSessionResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_net_lp_rpc_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*OrchestratorRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_net_lp_rpc_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*OSInfo); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_net_lp_rpc_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*S3OSInfo); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_net_lp_rpc_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PriceInfo); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_net_lp_rpc_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Capabilities); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_net_lp_rpc_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*OrchestratorInfo); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_net_lp_rpc_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AuthToken); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_net_lp_rpc_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SegData); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_net_lp_rpc_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SegParameters); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_net_lp_rpc_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VideoProfile); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_net_lp_rpc_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TranscodedSegmentData); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_net_lp_rpc_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TranscodeData); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_net_lp_rpc_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TranscodeResult); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_net_lp_rpc_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RegisterRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_net_lp_rpc_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NotifySegment); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_net_lp_rpc_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TicketParams); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_net_lp_rpc_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TicketSenderParams); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_net_lp_rpc_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TicketExpirationParams); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_net_lp_rpc_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Payment); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_net_lp_rpc_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Capabilities_Constraints); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_net_lp_rpc_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Capabilities_Constraints_ModelConstraint); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - file_net_lp_rpc_proto_msgTypes[15].OneofWrappers = []interface{}{ - (*TranscodeResult_Error)(nil), - (*TranscodeResult_Data)(nil), - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_net_lp_rpc_proto_rawDesc, - NumEnums: 5, - NumMessages: 27, - NumExtensions: 0, - NumServices: 2, - }, - GoTypes: file_net_lp_rpc_proto_goTypes, - DependencyIndexes: file_net_lp_rpc_proto_depIdxs, - EnumInfos: file_net_lp_rpc_proto_enumTypes, - MessageInfos: file_net_lp_rpc_proto_msgTypes, - }.Build() - File_net_lp_rpc_proto = out.File - file_net_lp_rpc_proto_rawDesc = nil - file_net_lp_rpc_proto_goTypes = nil - file_net_lp_rpc_proto_depIdxs = nil +func init() { + proto.RegisterEnum("net.OSInfo_StorageType", OSInfo_StorageType_name, OSInfo_StorageType_value) + proto.RegisterEnum("net.VideoProfile_Format", VideoProfile_Format_name, VideoProfile_Format_value) + proto.RegisterEnum("net.VideoProfile_Profile", VideoProfile_Profile_name, VideoProfile_Profile_value) + proto.RegisterEnum("net.VideoProfile_VideoCodec", VideoProfile_VideoCodec_name, VideoProfile_VideoCodec_value) + proto.RegisterEnum("net.VideoProfile_ChromaSubsampling", VideoProfile_ChromaSubsampling_name, VideoProfile_ChromaSubsampling_value) + proto.RegisterType((*PingPong)(nil), "net.PingPong") + proto.RegisterType((*EndTranscodingSessionRequest)(nil), "net.EndTranscodingSessionRequest") + proto.RegisterType((*EndTranscodingSessionResponse)(nil), "net.EndTranscodingSessionResponse") + proto.RegisterType((*OrchestratorRequest)(nil), "net.OrchestratorRequest") + proto.RegisterType((*OSInfo)(nil), "net.OSInfo") + proto.RegisterType((*S3OSInfo)(nil), "net.S3OSInfo") + proto.RegisterType((*PriceInfo)(nil), "net.PriceInfo") + proto.RegisterType((*Capabilities)(nil), "net.Capabilities") + proto.RegisterMapType((map[uint32]*Capabilities_CapabilityConstraints)(nil), "net.Capabilities.CapabilityConstraintsEntry") + proto.RegisterMapType((map[uint32]uint32)(nil), "net.Capabilities.CapacitiesEntry") + proto.RegisterType((*Capabilities_Constraints)(nil), "net.Capabilities.Constraints") + proto.RegisterType((*Capabilities_CapabilityConstraints)(nil), "net.Capabilities.CapabilityConstraints") + proto.RegisterMapType((map[string]*Capabilities_CapabilityConstraints_ModelConstraint)(nil), "net.Capabilities.CapabilityConstraints.ModelsEntry") + proto.RegisterType((*Capabilities_CapabilityConstraints_ModelConstraint)(nil), "net.Capabilities.CapabilityConstraints.ModelConstraint") + proto.RegisterType((*OrchestratorInfo)(nil), "net.OrchestratorInfo") + proto.RegisterType((*AuthToken)(nil), "net.AuthToken") + proto.RegisterType((*SegData)(nil), "net.SegData") + proto.RegisterType((*SegParameters)(nil), "net.SegParameters") + proto.RegisterType((*VideoProfile)(nil), "net.VideoProfile") + proto.RegisterType((*TranscodedSegmentData)(nil), "net.TranscodedSegmentData") + proto.RegisterType((*TranscodeData)(nil), "net.TranscodeData") + proto.RegisterType((*TranscodeResult)(nil), "net.TranscodeResult") + proto.RegisterType((*RegisterRequest)(nil), "net.RegisterRequest") + proto.RegisterType((*NotifySegment)(nil), "net.NotifySegment") + proto.RegisterType((*TicketParams)(nil), "net.TicketParams") + proto.RegisterType((*TicketSenderParams)(nil), "net.TicketSenderParams") + proto.RegisterType((*TicketExpirationParams)(nil), "net.TicketExpirationParams") + proto.RegisterType((*Payment)(nil), "net.Payment") +} + +func init() { + proto.RegisterFile("net/lp_rpc.proto", fileDescriptor_034e29c79f9ba827) +} + +var fileDescriptor_034e29c79f9ba827 = []byte{ + // 2021 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x58, 0xdd, 0x72, 0xdb, 0xc6, + 0xf5, 0x17, 0x3f, 0xc4, 0x8f, 0x43, 0x52, 0x82, 0xd6, 0x96, 0x0c, 0x33, 0x76, 0xfe, 0x32, 0x12, + 0xe5, 0xef, 0xcc, 0xd4, 0x8a, 0x87, 0x92, 0x9d, 0xb8, 0x33, 0x99, 0x56, 0x1f, 0xb4, 0xc4, 0xd4, + 0x92, 0x38, 0x4b, 0xd9, 0x33, 0xed, 0x45, 0x59, 0x08, 0x58, 0x92, 0xa8, 0x48, 0x00, 0x5a, 0x2c, + 0x63, 0x29, 0xd3, 0x17, 0xe8, 0x23, 0xb4, 0x37, 0x9d, 0xe9, 0x4c, 0x9f, 0xa0, 0x2f, 0xd0, 0x07, + 0xe8, 0x03, 0xf4, 0x41, 0x7a, 0xdf, 0xce, 0x9e, 0x5d, 0x80, 0x80, 0x48, 0x27, 0xaa, 0xef, 0xf6, + 0x7c, 0xee, 0xd9, 0xb3, 0x67, 0x7f, 0xe7, 0x00, 0x60, 0xf8, 0x4c, 0x7c, 0x35, 0x0e, 0xfb, 0x3c, + 0x74, 0xb6, 0x43, 0x1e, 0x88, 0x80, 0x14, 0x7c, 0x26, 0xac, 0x4d, 0xa8, 0x74, 0x3d, 0x7f, 0xd8, + 0x0d, 0xfc, 0x21, 0xb9, 0x0f, 0xcb, 0xdf, 0xdb, 0xe3, 0x29, 0x33, 0x73, 0x9b, 0xb9, 0xa7, 0x75, + 0xaa, 0x08, 0xeb, 0x04, 0x1e, 0xb5, 0x7d, 0xf7, 0x9c, 0xdb, 0x7e, 0xe4, 0x04, 0xae, 0xe7, 0x0f, + 0x7b, 0x2c, 0x8a, 0xbc, 0xc0, 0xa7, 0xec, 0x6a, 0xca, 0x22, 0x41, 0x9e, 0x01, 0xd8, 0x53, 0x31, + 0xea, 0x8b, 0xe0, 0x92, 0xf9, 0x68, 0x5a, 0x6b, 0xad, 0x6c, 0xfb, 0x4c, 0x6c, 0xef, 0x4d, 0xc5, + 0xe8, 0x5c, 0x72, 0x69, 0xd5, 0x8e, 0x97, 0xd6, 0xff, 0xc1, 0xe3, 0x0f, 0xb8, 0x8b, 0xc2, 0xc0, + 0x8f, 0x98, 0x75, 0x0d, 0xf7, 0xce, 0xb8, 0x33, 0x62, 0x91, 0xe0, 0xb6, 0x08, 0x78, 0xbc, 0x8d, + 0x09, 0x65, 0xdb, 0x75, 0x39, 0x8b, 0x22, 0x1d, 0x5e, 0x4c, 0x12, 0x03, 0x0a, 0x91, 0x37, 0x34, + 0xf3, 0xc8, 0x95, 0x4b, 0xf2, 0x02, 0xea, 0x8e, 0x1d, 0xda, 0x17, 0xde, 0xd8, 0x13, 0x1e, 0x8b, + 0xcc, 0x02, 0x06, 0xb5, 0x86, 0x41, 0x1d, 0xa4, 0x04, 0x34, 0xa3, 0x66, 0xfd, 0x29, 0x07, 0xa5, + 0xb3, 0x5e, 0xc7, 0x1f, 0x04, 0xe4, 0x15, 0xd4, 0x22, 0x11, 0x70, 0x7b, 0xc8, 0xce, 0x6f, 0x42, + 0x95, 0x90, 0x95, 0xd6, 0x03, 0x74, 0xa0, 0x34, 0xb6, 0x7b, 0x33, 0x31, 0x4d, 0xeb, 0x92, 0x2d, + 0x28, 0x45, 0x3b, 0x9e, 0x3f, 0x08, 0x4c, 0x03, 0xb7, 0x6d, 0xa0, 0x55, 0x6f, 0x47, 0xd9, 0x51, + 0x2d, 0xb4, 0x9e, 0x41, 0x2d, 0xe5, 0x82, 0x00, 0x94, 0x0e, 0x3b, 0xb4, 0x7d, 0x70, 0x6e, 0x2c, + 0x91, 0x12, 0xe4, 0x7b, 0x3b, 0x46, 0x4e, 0xf2, 0x8e, 0xce, 0xce, 0x8e, 0xde, 0xb4, 0x8d, 0xbc, + 0xf5, 0xd7, 0x1c, 0x54, 0x62, 0x1f, 0x84, 0x40, 0x71, 0x14, 0x44, 0x02, 0xc3, 0xaa, 0x52, 0x5c, + 0xcb, 0x2c, 0x5c, 0xb2, 0x1b, 0xcc, 0x42, 0x95, 0xca, 0x25, 0xd9, 0x80, 0x52, 0x18, 0x8c, 0x3d, + 0xe7, 0x06, 0xcf, 0x5f, 0xa5, 0x9a, 0x22, 0x8f, 0xa0, 0x1a, 0x79, 0x43, 0xdf, 0x16, 0x53, 0xce, + 0xcc, 0x22, 0x8a, 0x66, 0x0c, 0xf2, 0x29, 0x80, 0xc3, 0x99, 0xcb, 0x7c, 0xe1, 0xd9, 0x63, 0x73, + 0x19, 0xc5, 0x29, 0x0e, 0x69, 0x42, 0xe5, 0x7a, 0x6f, 0xf2, 0xc3, 0xa1, 0x2d, 0x98, 0x59, 0x42, + 0x69, 0x42, 0x5b, 0x6f, 0xa1, 0xda, 0xe5, 0x9e, 0xc3, 0x30, 0x48, 0x0b, 0xea, 0xa1, 0x24, 0xba, + 0x8c, 0xbf, 0xf5, 0x3d, 0x15, 0x6c, 0x81, 0x66, 0x78, 0xe4, 0x73, 0x68, 0x84, 0xde, 0x35, 0x1b, + 0x47, 0xb1, 0x52, 0x1e, 0x95, 0xb2, 0x4c, 0xeb, 0xef, 0x25, 0xa8, 0xa7, 0xaf, 0x4d, 0x9e, 0xe0, + 0xc2, 0x13, 0x91, 0xe0, 0x9e, 0x3f, 0x34, 0x73, 0x9b, 0x85, 0xa7, 0x45, 0x3a, 0x63, 0x90, 0x4d, + 0xa8, 0x4d, 0x6c, 0xdf, 0x95, 0xc5, 0x23, 0x2f, 0x3f, 0x8f, 0xf2, 0x34, 0x8b, 0xec, 0x01, 0xc8, + 0x8b, 0x77, 0xe2, 0xea, 0x28, 0x3c, 0xad, 0xb5, 0x9e, 0xcc, 0x55, 0x07, 0x12, 0x4a, 0xa7, 0xed, + 0x0b, 0x7e, 0x43, 0x53, 0x46, 0xb2, 0x1c, 0xbf, 0x67, 0x5c, 0x16, 0xae, 0x4e, 0x61, 0x4c, 0x92, + 0x5f, 0x40, 0xcd, 0x09, 0x7c, 0x59, 0xbd, 0x9e, 0x2f, 0x22, 0xcc, 0x60, 0xad, 0xf5, 0x78, 0x81, + 0xf7, 0x99, 0x12, 0x4d, 0x5b, 0x90, 0x0b, 0x58, 0x4f, 0xca, 0xf2, 0x26, 0xa5, 0x65, 0x96, 0x30, + 0xd0, 0x9f, 0x2d, 0x0e, 0x74, 0x4e, 0x5d, 0xc5, 0xbc, 0xd8, 0x55, 0xf3, 0x5b, 0x58, 0xbd, 0x75, + 0xba, 0xb8, 0x80, 0xe4, 0x35, 0x35, 0x54, 0x01, 0x25, 0x78, 0x90, 0x47, 0x9e, 0x22, 0x7e, 0x9e, + 0xff, 0x26, 0xd7, 0x7c, 0x06, 0xb5, 0x94, 0x37, 0x59, 0x33, 0x13, 0xcf, 0x7f, 0xa7, 0xf3, 0xa1, + 0xaa, 0x32, 0xc5, 0x69, 0xfe, 0x27, 0x07, 0xeb, 0x0b, 0x63, 0x24, 0xbf, 0x82, 0xd2, 0x24, 0x70, + 0xd9, 0x38, 0xc2, 0x6b, 0xac, 0xb5, 0x76, 0xee, 0x78, 0xb8, 0xed, 0x13, 0xb4, 0x52, 0x67, 0xd4, + 0x2e, 0x9a, 0x5b, 0xb0, 0x8a, 0xec, 0x99, 0x9e, 0x7c, 0x29, 0xef, 0x6d, 0x3e, 0xc1, 0x98, 0x2a, + 0x14, 0xd7, 0x4d, 0x0e, 0xb5, 0x94, 0x75, 0xfa, 0xdc, 0xfa, 0xe1, 0x9c, 0xa4, 0xcf, 0x5d, 0x6b, + 0x7d, 0xfd, 0x3f, 0xc5, 0x34, 0x63, 0xa4, 0x13, 0x76, 0x05, 0xcd, 0x0f, 0x5f, 0xd2, 0x82, 0xd4, + 0x7f, 0x9b, 0x0d, 0xe1, 0xff, 0xef, 0x18, 0x42, 0x6a, 0x4b, 0xeb, 0x1f, 0x79, 0x30, 0xd2, 0x40, + 0x8a, 0x8f, 0xf2, 0x53, 0x00, 0xa1, 0xa1, 0x97, 0xf1, 0xf8, 0xa6, 0x66, 0x1c, 0xf2, 0x12, 0x1a, + 0xc2, 0x73, 0x2e, 0x99, 0xe8, 0x87, 0x36, 0xb7, 0x27, 0x91, 0xde, 0x5f, 0x41, 0xe7, 0x39, 0x4a, + 0xba, 0x28, 0xa0, 0x75, 0x91, 0xa2, 0x64, 0x13, 0xc0, 0x87, 0xdd, 0x47, 0xe0, 0x2b, 0xa4, 0x9a, + 0x40, 0x02, 0x08, 0xb4, 0x1a, 0x26, 0xd8, 0x90, 0x02, 0xf3, 0x62, 0x16, 0xcc, 0x6f, 0x43, 0xf7, + 0xf2, 0x9d, 0xa0, 0xfb, 0x56, 0x13, 0x2a, 0xfd, 0x44, 0x13, 0x22, 0x5b, 0x50, 0xd6, 0x90, 0x6d, + 0x6e, 0x62, 0xdd, 0xd5, 0x52, 0xd0, 0x4e, 0x63, 0x99, 0xf5, 0x3b, 0xa8, 0x26, 0xe6, 0xf2, 0x35, + 0xcc, 0x5a, 0x5c, 0x9d, 0x2a, 0x82, 0x3c, 0x06, 0x88, 0x54, 0x03, 0xeb, 0x7b, 0xae, 0x46, 0xdf, + 0xaa, 0xe6, 0x74, 0x5c, 0x99, 0x6f, 0x76, 0x1d, 0x7a, 0xdc, 0x16, 0xf2, 0x65, 0x14, 0x10, 0xdd, + 0x52, 0x1c, 0xeb, 0xdf, 0x45, 0x28, 0xf7, 0xd8, 0xf0, 0xd0, 0x16, 0x36, 0xbe, 0x22, 0xdb, 0xf7, + 0x06, 0x2c, 0x12, 0x1d, 0x57, 0xef, 0x92, 0xe2, 0x60, 0x9f, 0x63, 0x57, 0x1a, 0x22, 0xe5, 0x12, + 0xfb, 0x80, 0x1d, 0x8d, 0xd0, 0x6f, 0x9d, 0xe2, 0x5a, 0xe2, 0x73, 0xc8, 0x83, 0x81, 0x37, 0x66, + 0x71, 0x6e, 0x13, 0x3a, 0xee, 0x94, 0xcb, 0xb3, 0x4e, 0xd9, 0x84, 0x8a, 0x3b, 0xd5, 0xd1, 0xc9, + 0xac, 0x2d, 0xd3, 0x84, 0x9e, 0xbb, 0x8a, 0xf2, 0xc7, 0x5c, 0x45, 0xe5, 0xa7, 0xae, 0xe2, 0x39, + 0xdc, 0x77, 0xec, 0xb1, 0xd3, 0x0f, 0x19, 0x77, 0x58, 0x28, 0xa6, 0xf6, 0xb8, 0x8f, 0x67, 0x02, + 0x7c, 0xb1, 0x44, 0xca, 0xba, 0x89, 0xe8, 0x58, 0x9e, 0xf0, 0x6e, 0x97, 0x27, 0xc3, 0x1f, 0x4c, + 0xc7, 0xe3, 0x6e, 0x9c, 0x8c, 0x27, 0xa8, 0xab, 0xc2, 0x7f, 0xe7, 0xb9, 0x2c, 0xd0, 0x12, 0x9a, + 0x51, 0x23, 0x5f, 0x43, 0x23, 0x4d, 0xb7, 0x4c, 0xeb, 0x43, 0x76, 0x59, 0xbd, 0xdb, 0x86, 0x3b, + 0xe6, 0x67, 0x77, 0x32, 0xdc, 0x21, 0x7b, 0x40, 0x22, 0x36, 0x9c, 0x30, 0x5f, 0x3f, 0x3a, 0x26, + 0x18, 0x8f, 0xcc, 0x2d, 0x4c, 0x1c, 0x51, 0xc3, 0x03, 0x1b, 0x76, 0x13, 0x09, 0x5d, 0xd3, 0xda, + 0x33, 0x16, 0xd9, 0x06, 0xf2, 0x3a, 0xe0, 0x0e, 0x4b, 0x66, 0x29, 0x4f, 0x36, 0xd3, 0x2f, 0x54, + 0x0a, 0xe7, 0x25, 0xd6, 0x0e, 0x34, 0x32, 0x3e, 0x65, 0x25, 0x0d, 0x78, 0xa0, 0x70, 0xb2, 0x48, + 0x71, 0x4d, 0x56, 0x20, 0x2f, 0x02, 0x2c, 0xb7, 0x22, 0xcd, 0x8b, 0xc0, 0xfa, 0xe7, 0x32, 0xd4, + 0xd3, 0xe7, 0x90, 0x46, 0xbe, 0x3d, 0x61, 0x38, 0xe7, 0x54, 0x29, 0xae, 0xe5, 0x2b, 0x79, 0xef, + 0xb9, 0x62, 0x64, 0xae, 0x61, 0x35, 0x29, 0x42, 0x8e, 0x22, 0x23, 0xe6, 0x0d, 0x47, 0xc2, 0x24, + 0xc8, 0xd6, 0x94, 0xc4, 0x81, 0x0b, 0x4f, 0xc2, 0x13, 0x33, 0xef, 0xa1, 0x20, 0x26, 0x65, 0xa9, + 0x0e, 0xc2, 0xc8, 0xbc, 0xaf, 0x20, 0x71, 0x10, 0x46, 0xe4, 0x39, 0x94, 0x06, 0x01, 0x9f, 0xd8, + 0xc2, 0x5c, 0xc7, 0x69, 0xcc, 0x9c, 0x4b, 0xec, 0xf6, 0x6b, 0x94, 0x53, 0xad, 0x27, 0x77, 0x1d, + 0x84, 0xd1, 0x21, 0xf3, 0xcd, 0x0d, 0x74, 0xa3, 0x29, 0xb2, 0x03, 0x65, 0xfd, 0x24, 0xcc, 0x07, + 0xe8, 0xea, 0xe1, 0xbc, 0xab, 0xf8, 0xae, 0x62, 0x4d, 0x19, 0xd0, 0x30, 0x08, 0x4d, 0x13, 0xc3, + 0x94, 0x4b, 0xf2, 0x12, 0xca, 0xcc, 0x57, 0x40, 0xfa, 0x10, 0xdd, 0x3c, 0x9a, 0x77, 0x83, 0xc4, + 0x41, 0xe0, 0x32, 0x87, 0xc6, 0xca, 0x38, 0x61, 0x05, 0xe3, 0x80, 0x1f, 0xb2, 0x50, 0x8c, 0xcc, + 0x26, 0x3a, 0x4c, 0x71, 0xc8, 0x11, 0xd4, 0x9d, 0x11, 0x0f, 0x26, 0xb6, 0x3a, 0x8e, 0xf9, 0x09, + 0x3a, 0xff, 0x6c, 0xde, 0xf9, 0x01, 0x6a, 0xf5, 0xa6, 0x17, 0x91, 0x3d, 0x09, 0xc7, 0x9e, 0x3f, + 0xa4, 0x19, 0x43, 0x99, 0xdd, 0xab, 0xa9, 0x2d, 0x5b, 0x84, 0xf9, 0x08, 0x13, 0x10, 0x93, 0xd6, + 0x63, 0x28, 0x69, 0x1d, 0x80, 0xd2, 0x49, 0xb7, 0x7d, 0x74, 0xde, 0x33, 0x96, 0x48, 0x19, 0x0a, + 0x27, 0xdd, 0x5d, 0x23, 0x67, 0xfd, 0x1e, 0xca, 0xf1, 0x1d, 0xdf, 0x83, 0xd5, 0xf6, 0xe9, 0xc1, + 0xd9, 0x61, 0x9b, 0xf6, 0x0f, 0xdb, 0xaf, 0xf7, 0xde, 0xbe, 0x91, 0x03, 0xea, 0x1a, 0x34, 0x8e, + 0x5b, 0x2f, 0x77, 0xfb, 0xfb, 0x7b, 0xbd, 0xf6, 0x9b, 0xce, 0x69, 0xdb, 0xc8, 0x91, 0x06, 0x54, + 0x91, 0x75, 0xb2, 0xd7, 0x39, 0x35, 0xf2, 0x09, 0x79, 0xdc, 0x39, 0x3a, 0x36, 0x0a, 0xe4, 0x21, + 0xac, 0x23, 0x79, 0x70, 0x76, 0xda, 0x3b, 0xa7, 0x7b, 0x9d, 0xd3, 0xf6, 0xa1, 0x12, 0x15, 0xad, + 0x16, 0xc0, 0x2c, 0x49, 0xa4, 0x02, 0x45, 0xa9, 0x68, 0x2c, 0xe9, 0xd5, 0x0b, 0x23, 0x27, 0xc3, + 0x7a, 0xd7, 0xfd, 0xc6, 0xc8, 0xab, 0xc5, 0x2b, 0xa3, 0x60, 0x1d, 0xc0, 0xda, 0xdc, 0xd9, 0xc9, + 0x0a, 0xc0, 0xc1, 0x31, 0x3d, 0x3b, 0xd9, 0xeb, 0xef, 0xb6, 0x9e, 0x1b, 0x4b, 0x19, 0xba, 0x65, + 0xe4, 0xd2, 0xf4, 0xee, 0xae, 0x91, 0xb7, 0xae, 0x60, 0x3d, 0xfe, 0x0a, 0x61, 0x6e, 0x4f, 0x3d, + 0x29, 0xc4, 0x61, 0x03, 0x0a, 0x53, 0x3e, 0x8e, 0x07, 0x82, 0x29, 0x1f, 0xe3, 0x24, 0x8d, 0x13, + 0xa9, 0x06, 0x5f, 0x4d, 0x91, 0x6d, 0xb8, 0x77, 0x0b, 0xb6, 0xfa, 0xd2, 0x52, 0x8d, 0xdb, 0x6b, + 0x61, 0x06, 0xb6, 0xde, 0xf2, 0xb1, 0xf5, 0x6b, 0x68, 0x24, 0x5b, 0xe2, 0x56, 0x2f, 0xa1, 0xa2, + 0x1f, 0x73, 0x3c, 0x00, 0x35, 0x55, 0xa7, 0x5d, 0x14, 0x18, 0x4d, 0x74, 0xe7, 0x3f, 0x79, 0xac, + 0x3f, 0xe7, 0x60, 0x35, 0xb1, 0xa2, 0x2c, 0x9a, 0x8e, 0x45, 0xdc, 0x30, 0x72, 0xb3, 0x86, 0xb1, + 0x01, 0xcb, 0x8c, 0xf3, 0x80, 0xab, 0x46, 0x75, 0xbc, 0x44, 0x15, 0x49, 0x9e, 0x42, 0xd1, 0xb5, + 0x85, 0xad, 0x1b, 0x37, 0xc9, 0xc6, 0x20, 0xf7, 0x3e, 0x5e, 0xa2, 0xa8, 0x41, 0xbe, 0x84, 0x62, + 0xea, 0xdb, 0x66, 0x5d, 0x21, 0xef, 0xad, 0x29, 0x83, 0xa2, 0xca, 0x7e, 0x05, 0x4a, 0x1c, 0x03, + 0xb1, 0xfe, 0x00, 0xab, 0x94, 0x0d, 0xbd, 0x48, 0xb0, 0xe4, 0x73, 0x6e, 0x03, 0x4a, 0x11, 0x73, + 0x38, 0x8b, 0x3f, 0x62, 0x34, 0x25, 0x1b, 0x92, 0x9e, 0xb2, 0x6f, 0x74, 0xb2, 0x13, 0xfa, 0x63, + 0x3f, 0xeb, 0xfe, 0x98, 0x83, 0xc6, 0x69, 0x20, 0xbc, 0xc1, 0x8d, 0x4e, 0xe6, 0x82, 0x1b, 0xfe, + 0x02, 0xca, 0x91, 0x6a, 0xc3, 0xda, 0x6b, 0x3d, 0x06, 0x5e, 0xcc, 0x7c, 0x2c, 0x94, 0x61, 0x0b, + 0x3b, 0xba, 0xec, 0xb8, 0x98, 0x80, 0x02, 0xd5, 0x54, 0xa6, 0xeb, 0xae, 0x65, 0xbb, 0xee, 0x77, + 0xc5, 0x4a, 0xde, 0x28, 0x7c, 0x57, 0xac, 0x3c, 0x31, 0x2c, 0xeb, 0x2f, 0x79, 0xa8, 0xa7, 0xc7, + 0x28, 0xf9, 0x29, 0xc3, 0x99, 0xe3, 0x85, 0x1e, 0xf3, 0x85, 0xee, 0xf9, 0x33, 0x86, 0x9c, 0x2e, + 0x06, 0xb6, 0xc3, 0xfa, 0xb3, 0x59, 0xb0, 0x4e, 0xab, 0x92, 0xf3, 0x4e, 0x32, 0xc8, 0x43, 0xa8, + 0xbc, 0xf7, 0xfc, 0x7e, 0xc8, 0x83, 0x0b, 0x3d, 0x03, 0x94, 0xdf, 0x7b, 0x7e, 0x97, 0x07, 0x17, + 0xb2, 0x34, 0x13, 0x37, 0x7d, 0x6e, 0xfb, 0xae, 0xea, 0xaa, 0x6a, 0x22, 0x58, 0x4b, 0x44, 0xd4, + 0xf6, 0x5d, 0x6c, 0xaa, 0x04, 0x8a, 0x11, 0x63, 0xae, 0x9e, 0x0d, 0x70, 0x4d, 0xbe, 0x04, 0x63, + 0x36, 0xaa, 0xf4, 0x2f, 0xc6, 0x81, 0x73, 0x89, 0x43, 0x42, 0x9d, 0xae, 0xce, 0xf8, 0xfb, 0x92, + 0x4d, 0x8e, 0x61, 0x2d, 0xa5, 0xaa, 0x67, 0x47, 0x35, 0x30, 0x7c, 0x92, 0x9a, 0x1d, 0xdb, 0x89, + 0x8e, 0x9e, 0x22, 0x53, 0x1b, 0x28, 0x8e, 0xd5, 0x01, 0xa2, 0x74, 0x7b, 0xcc, 0x77, 0x19, 0xd7, + 0x69, 0x7a, 0x02, 0xf5, 0x08, 0xe9, 0xbe, 0x1f, 0xf8, 0x0e, 0xd3, 0xa3, 0x72, 0x4d, 0xf1, 0x4e, + 0x25, 0x6b, 0xc1, 0x9b, 0xf8, 0x01, 0x36, 0x16, 0x6f, 0x4b, 0xb6, 0x60, 0xc5, 0xe1, 0x4c, 0x05, + 0xcb, 0x83, 0xa9, 0xef, 0xea, 0x47, 0xd2, 0x88, 0xb9, 0x54, 0x32, 0xc9, 0x2b, 0x78, 0x98, 0x55, + 0x53, 0x49, 0x50, 0xa9, 0x54, 0x1b, 0x6d, 0x64, 0x2c, 0x30, 0x19, 0x32, 0x9f, 0xd6, 0xdf, 0xf2, + 0x50, 0xee, 0xda, 0x37, 0x58, 0x6e, 0x73, 0x43, 0x75, 0xee, 0x6e, 0x43, 0x35, 0xbe, 0x11, 0x79, + 0x40, 0xbd, 0x97, 0xa6, 0x16, 0x27, 0xbb, 0xf0, 0x11, 0xc9, 0x26, 0x1d, 0xb8, 0xaf, 0x23, 0xd3, + 0xd9, 0xd5, 0xce, 0x8a, 0x88, 0x45, 0x0f, 0x52, 0xce, 0xd2, 0xb7, 0x41, 0x89, 0x98, 0xbf, 0xa1, + 0x17, 0xb0, 0xc2, 0xae, 0x43, 0xe6, 0x08, 0xe6, 0xf6, 0x71, 0xd0, 0xd7, 0xa3, 0xfb, 0xed, 0xaf, + 0x80, 0x46, 0xac, 0x85, 0xac, 0xd6, 0xbf, 0x72, 0x50, 0x4f, 0xe3, 0x07, 0xd9, 0x87, 0xd5, 0x23, + 0x26, 0x32, 0x2c, 0x73, 0x0e, 0x65, 0x34, 0x8a, 0x34, 0x17, 0xe3, 0x0f, 0xf9, 0x2d, 0xac, 0x2f, + 0xfc, 0xc7, 0x44, 0xd4, 0x47, 0xfe, 0x8f, 0xfd, 0xce, 0x6a, 0x5a, 0x3f, 0xa6, 0xa2, 0x7e, 0x51, + 0x91, 0xcf, 0xa1, 0xd8, 0x95, 0x2d, 0x47, 0xfd, 0xda, 0x89, 0xff, 0x9f, 0x35, 0xb3, 0x64, 0xeb, + 0x14, 0xe0, 0x7c, 0xf6, 0x65, 0xf5, 0x4b, 0x20, 0x31, 0x06, 0xa6, 0xb8, 0xf7, 0xd1, 0xe4, 0x16, + 0x38, 0x36, 0x15, 0x00, 0x67, 0x30, 0xeb, 0x79, 0x6e, 0xbf, 0xfc, 0x9b, 0xe5, 0xed, 0xaf, 0x7c, + 0x26, 0x2e, 0x4a, 0xf8, 0xff, 0x6e, 0xe7, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xa2, 0xc3, 0xc1, + 0x2e, 0xd3, 0x13, 0x00, 0x00, } diff --git a/net/lp_rpc.proto b/net/lp_rpc.proto index 0eddc9fee..bd3e6f1ef 100644 --- a/net/lp_rpc.proto +++ b/net/lp_rpc.proto @@ -108,8 +108,17 @@ message Capabilities { // Capacity corresponding to each capability map capacities = 3; - // Non-binary capability constraints, such as supported ranges. + string version = 4; + + Constraints constraints = 5; + + // Non-binary general constraints. message Constraints { + string minVersion = 1; + } + + // Non-binary capability constraints, such as supported ranges. + message CapabilityConstraints { message ModelConstraint { bool warm = 1; } @@ -117,7 +126,7 @@ message Capabilities { map models = 1; } - map constraints = 4; + map capabilityConstraints = 6; } // The orchestrator sends this in response to `GetOrchestrator`, containing diff --git a/net/lp_rpc_grpc.pb.go b/net/lp_rpc_grpc.pb.go index 3743d7975..fa1d80d2e 100644 --- a/net/lp_rpc_grpc.pb.go +++ b/net/lp_rpc_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.2.0 -// - protoc v4.25.2 +// - protoc-gen-go-grpc v1.5.1 +// - protoc v3.21.12 // source: net/lp_rpc.proto package net @@ -15,12 +15,20 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + Orchestrator_GetOrchestrator_FullMethodName = "/net.Orchestrator/GetOrchestrator" + Orchestrator_EndTranscodingSession_FullMethodName = "/net.Orchestrator/EndTranscodingSession" + Orchestrator_Ping_FullMethodName = "/net.Orchestrator/Ping" +) // OrchestratorClient is the client API for Orchestrator service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// RPC calls implemented by the orchestrator type OrchestratorClient interface { // Called by the broadcaster to request transcoder info from an orchestrator. GetOrchestrator(ctx context.Context, in *OrchestratorRequest, opts ...grpc.CallOption) (*OrchestratorInfo, error) @@ -37,8 +45,9 @@ func NewOrchestratorClient(cc grpc.ClientConnInterface) OrchestratorClient { } func (c *orchestratorClient) GetOrchestrator(ctx context.Context, in *OrchestratorRequest, opts ...grpc.CallOption) (*OrchestratorInfo, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(OrchestratorInfo) - err := c.cc.Invoke(ctx, "/net.Orchestrator/GetOrchestrator", in, out, opts...) + err := c.cc.Invoke(ctx, Orchestrator_GetOrchestrator_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -46,8 +55,9 @@ func (c *orchestratorClient) GetOrchestrator(ctx context.Context, in *Orchestrat } func (c *orchestratorClient) EndTranscodingSession(ctx context.Context, in *EndTranscodingSessionRequest, opts ...grpc.CallOption) (*EndTranscodingSessionResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(EndTranscodingSessionResponse) - err := c.cc.Invoke(ctx, "/net.Orchestrator/EndTranscodingSession", in, out, opts...) + err := c.cc.Invoke(ctx, Orchestrator_EndTranscodingSession_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -55,8 +65,9 @@ func (c *orchestratorClient) EndTranscodingSession(ctx context.Context, in *EndT } func (c *orchestratorClient) Ping(ctx context.Context, in *PingPong, opts ...grpc.CallOption) (*PingPong, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(PingPong) - err := c.cc.Invoke(ctx, "/net.Orchestrator/Ping", in, out, opts...) + err := c.cc.Invoke(ctx, Orchestrator_Ping_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -65,7 +76,9 @@ func (c *orchestratorClient) Ping(ctx context.Context, in *PingPong, opts ...grp // OrchestratorServer is the server API for Orchestrator service. // All implementations must embed UnimplementedOrchestratorServer -// for forward compatibility +// for forward compatibility. +// +// RPC calls implemented by the orchestrator type OrchestratorServer interface { // Called by the broadcaster to request transcoder info from an orchestrator. GetOrchestrator(context.Context, *OrchestratorRequest) (*OrchestratorInfo, error) @@ -74,9 +87,12 @@ type OrchestratorServer interface { mustEmbedUnimplementedOrchestratorServer() } -// UnimplementedOrchestratorServer must be embedded to have forward compatible implementations. -type UnimplementedOrchestratorServer struct { -} +// UnimplementedOrchestratorServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedOrchestratorServer struct{} func (UnimplementedOrchestratorServer) GetOrchestrator(context.Context, *OrchestratorRequest) (*OrchestratorInfo, error) { return nil, status.Errorf(codes.Unimplemented, "method GetOrchestrator not implemented") @@ -88,6 +104,7 @@ func (UnimplementedOrchestratorServer) Ping(context.Context, *PingPong) (*PingPo return nil, status.Errorf(codes.Unimplemented, "method Ping not implemented") } func (UnimplementedOrchestratorServer) mustEmbedUnimplementedOrchestratorServer() {} +func (UnimplementedOrchestratorServer) testEmbeddedByValue() {} // UnsafeOrchestratorServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to OrchestratorServer will @@ -97,6 +114,13 @@ type UnsafeOrchestratorServer interface { } func RegisterOrchestratorServer(s grpc.ServiceRegistrar, srv OrchestratorServer) { + // If the following call pancis, it indicates UnimplementedOrchestratorServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&Orchestrator_ServiceDesc, srv) } @@ -110,7 +134,7 @@ func _Orchestrator_GetOrchestrator_Handler(srv interface{}, ctx context.Context, } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/net.Orchestrator/GetOrchestrator", + FullMethod: Orchestrator_GetOrchestrator_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(OrchestratorServer).GetOrchestrator(ctx, req.(*OrchestratorRequest)) @@ -128,7 +152,7 @@ func _Orchestrator_EndTranscodingSession_Handler(srv interface{}, ctx context.Co } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/net.Orchestrator/EndTranscodingSession", + FullMethod: Orchestrator_EndTranscodingSession_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(OrchestratorServer).EndTranscodingSession(ctx, req.(*EndTranscodingSessionRequest)) @@ -146,7 +170,7 @@ func _Orchestrator_Ping_Handler(srv interface{}, ctx context.Context, dec func(i } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/net.Orchestrator/Ping", + FullMethod: Orchestrator_Ping_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(OrchestratorServer).Ping(ctx, req.(*PingPong)) @@ -178,13 +202,17 @@ var Orchestrator_ServiceDesc = grpc.ServiceDesc{ Metadata: "net/lp_rpc.proto", } +const ( + Transcoder_RegisterTranscoder_FullMethodName = "/net.Transcoder/RegisterTranscoder" +) + // TranscoderClient is the client API for Transcoder service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type TranscoderClient interface { // Called by the transcoder to register to an orchestrator. The orchestrator // notifies registered transcoders of segments as they come in. - RegisterTranscoder(ctx context.Context, in *RegisterRequest, opts ...grpc.CallOption) (Transcoder_RegisterTranscoderClient, error) + RegisterTranscoder(ctx context.Context, in *RegisterRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[NotifySegment], error) } type transcoderClient struct { @@ -195,12 +223,13 @@ func NewTranscoderClient(cc grpc.ClientConnInterface) TranscoderClient { return &transcoderClient{cc} } -func (c *transcoderClient) RegisterTranscoder(ctx context.Context, in *RegisterRequest, opts ...grpc.CallOption) (Transcoder_RegisterTranscoderClient, error) { - stream, err := c.cc.NewStream(ctx, &Transcoder_ServiceDesc.Streams[0], "/net.Transcoder/RegisterTranscoder", opts...) +func (c *transcoderClient) RegisterTranscoder(ctx context.Context, in *RegisterRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[NotifySegment], error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &Transcoder_ServiceDesc.Streams[0], Transcoder_RegisterTranscoder_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &transcoderRegisterTranscoderClient{stream} + x := &grpc.GenericClientStream[RegisterRequest, NotifySegment]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } @@ -210,41 +239,31 @@ func (c *transcoderClient) RegisterTranscoder(ctx context.Context, in *RegisterR return x, nil } -type Transcoder_RegisterTranscoderClient interface { - Recv() (*NotifySegment, error) - grpc.ClientStream -} - -type transcoderRegisterTranscoderClient struct { - grpc.ClientStream -} - -func (x *transcoderRegisterTranscoderClient) Recv() (*NotifySegment, error) { - m := new(NotifySegment) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type Transcoder_RegisterTranscoderClient = grpc.ServerStreamingClient[NotifySegment] // TranscoderServer is the server API for Transcoder service. // All implementations must embed UnimplementedTranscoderServer -// for forward compatibility +// for forward compatibility. type TranscoderServer interface { // Called by the transcoder to register to an orchestrator. The orchestrator // notifies registered transcoders of segments as they come in. - RegisterTranscoder(*RegisterRequest, Transcoder_RegisterTranscoderServer) error + RegisterTranscoder(*RegisterRequest, grpc.ServerStreamingServer[NotifySegment]) error mustEmbedUnimplementedTranscoderServer() } -// UnimplementedTranscoderServer must be embedded to have forward compatible implementations. -type UnimplementedTranscoderServer struct { -} +// UnimplementedTranscoderServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedTranscoderServer struct{} -func (UnimplementedTranscoderServer) RegisterTranscoder(*RegisterRequest, Transcoder_RegisterTranscoderServer) error { +func (UnimplementedTranscoderServer) RegisterTranscoder(*RegisterRequest, grpc.ServerStreamingServer[NotifySegment]) error { return status.Errorf(codes.Unimplemented, "method RegisterTranscoder not implemented") } func (UnimplementedTranscoderServer) mustEmbedUnimplementedTranscoderServer() {} +func (UnimplementedTranscoderServer) testEmbeddedByValue() {} // UnsafeTranscoderServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to TranscoderServer will @@ -254,6 +273,13 @@ type UnsafeTranscoderServer interface { } func RegisterTranscoderServer(s grpc.ServiceRegistrar, srv TranscoderServer) { + // If the following call pancis, it indicates UnimplementedTranscoderServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&Transcoder_ServiceDesc, srv) } @@ -262,21 +288,11 @@ func _Transcoder_RegisterTranscoder_Handler(srv interface{}, stream grpc.ServerS if err := stream.RecvMsg(m); err != nil { return err } - return srv.(TranscoderServer).RegisterTranscoder(m, &transcoderRegisterTranscoderServer{stream}) -} - -type Transcoder_RegisterTranscoderServer interface { - Send(*NotifySegment) error - grpc.ServerStream + return srv.(TranscoderServer).RegisterTranscoder(m, &grpc.GenericServerStream[RegisterRequest, NotifySegment]{ServerStream: stream}) } -type transcoderRegisterTranscoderServer struct { - grpc.ServerStream -} - -func (x *transcoderRegisterTranscoderServer) Send(m *NotifySegment) error { - return x.ServerStream.SendMsg(m) -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type Transcoder_RegisterTranscoderServer = grpc.ServerStreamingServer[NotifySegment] // Transcoder_ServiceDesc is the grpc.ServiceDesc for Transcoder service. // It's only intended for direct use with grpc.RegisterService, diff --git a/net/redeemer.pb.go b/net/redeemer.pb.go index 132a70cf4..87d1869b4 100644 --- a/net/redeemer.pb.go +++ b/net/redeemer.pb.go @@ -319,7 +319,6 @@ func file_net_redeemer_proto_init() { if File_net_redeemer_proto != nil { return } - file_net_lp_rpc_proto_init() if !protoimpl.UnsafeEnabled { file_net_redeemer_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Ticket); i { diff --git a/pm/recipient.go b/pm/recipient.go index 1c2333a5b..4c2028175 100644 --- a/pm/recipient.go +++ b/pm/recipient.go @@ -43,7 +43,7 @@ type Recipient interface { RedeemWinningTicket(ticket *Ticket, sig []byte, seed *big.Int) error // TicketParams returns the recipient's currently accepted ticket parameters - // for a provided sender ETH adddress + // for a provided sender ETH address TicketParams(sender ethcommon.Address, price *big.Rat) (*TicketParams, error) // TxCostMultiplier returns the tx cost multiplier for an address diff --git a/pm/sender.go b/pm/sender.go index db178bfc5..6f6acf0fd 100644 --- a/pm/sender.go +++ b/pm/sender.go @@ -113,7 +113,7 @@ func (s *sender) CreateTicketBatch(sessionID string, size int) (*TicketBatch, er ticketParams := &session.ticketParams expirationParams := ticketParams.ExpirationParams - // Ensure backwards compatbility + // Ensure backwards compatibility // If no expirationParams are included by O // B sets the values based upon its last seen round if expirationParams == nil || expirationParams.CreationRound == 0 || expirationParams.CreationRoundBlockHash == (ethcommon.Hash{}) { diff --git a/pm/stub.go b/pm/stub.go index 856bb9416..a3e0f7f22 100644 --- a/pm/stub.go +++ b/pm/stub.go @@ -466,7 +466,7 @@ func (m *MockRecipient) RedeemWinningTicket(ticket *Ticket, sig []byte, seed *bi } // TicketParams returns the recipient's currently accepted ticket parameters -// for a provided sender ETH adddress +// for a provided sender ETH address func (m *MockRecipient) TicketParams(sender ethcommon.Address, price *big.Rat) (*TicketParams, error) { args := m.Called(sender, price) diff --git a/server/ai_session.go b/server/ai_session.go index 6274e397d..3a6c28207 100644 --- a/server/ai_session.go +++ b/server/ai_session.go @@ -259,7 +259,7 @@ func (sel *AISessionSelector) Refresh(ctx context.Context) error { var coldSessions []*BroadcastSession for _, sess := range sessions { // If the constraints are missing for this capability skip this session - constraints, ok := sess.OrchestratorInfo.Capabilities.Constraints[uint32(sel.cap)] + constraints, ok := sess.OrchestratorInfo.Capabilities.CapabilityConstraints[uint32(sel.cap)] if !ok { continue } @@ -288,7 +288,7 @@ func (sel *AISessionSelector) Refresh(ctx context.Context) error { func (sel *AISessionSelector) getSessions(ctx context.Context) ([]*BroadcastSession, error) { // No warm constraints applied here because we don't want to filter out orchs based on warm criteria at discovery time // Instead, we want all orchs that support the model and then will filter for orchs that have a warm model separately - constraints := map[core.Capability]*core.Constraints{ + capabilityConstraints := map[core.Capability]*core.PerCapabilityConstraints{ sel.cap: { Models: map[string]*core.ModelConstraint{ sel.modelID: { @@ -297,7 +297,8 @@ func (sel *AISessionSelector) getSessions(ctx context.Context) ([]*BroadcastSess }, }, } - caps := core.NewCapabilitiesWithConstraints(append(core.DefaultCapabilities(), sel.cap), nil, constraints) + caps := core.NewCapabilitiesWithConstraints(append(core.DefaultCapabilities(), sel.cap), nil, core.Constraints{}, capabilityConstraints) + caps.SetMinVersionConstraint(sel.node.Capabilities.MinVersionConstraint()) // Set numOrchs to the pool size so that discovery tries to find maximum # of compatible orchs within a timeout numOrchs := sel.node.OrchestratorPool.Size() diff --git a/server/broadcast.go b/server/broadcast.go index bb0a8344f..df854cc48 100755 --- a/server/broadcast.go +++ b/server/broadcast.go @@ -56,7 +56,7 @@ var submitMultiSession = func(ctx context.Context, sess *BroadcastSession, seg * var maxTranscodeAttempts = errors.New("hit max transcode attempts") type BroadcastConfig struct { - maxPrice *big.Rat + maxPrice *core.AutoConvertedPrice mu sync.RWMutex } @@ -68,16 +68,19 @@ type SegFlightMetadata struct { func (cfg *BroadcastConfig) MaxPrice() *big.Rat { cfg.mu.RLock() defer cfg.mu.RUnlock() - return cfg.maxPrice + if cfg.maxPrice == nil { + return nil + } + return cfg.maxPrice.Value() } -func (cfg *BroadcastConfig) SetMaxPrice(price *big.Rat) { +func (cfg *BroadcastConfig) SetMaxPrice(price *core.AutoConvertedPrice) { cfg.mu.Lock() defer cfg.mu.Unlock() + prevPrice := cfg.maxPrice cfg.maxPrice = price - - if monitor.Enabled { - monitor.MaxTranscodingPrice(price) + if prevPrice != nil { + prevPrice.Stop() } } @@ -285,7 +288,9 @@ func (sp *SessionPool) selectSessions(ctx context.Context, sessionsNum int) []*B checkSessions := func(m *SessionPool) bool { numSess := m.sel.Size() - if numSess < int(math.Min(maxRefreshSessionsThreshold, math.Ceil(float64(m.numOrchs)/2.0))) { + refreshThreshold := int(math.Min(maxRefreshSessionsThreshold, math.Ceil(float64(m.numOrchs)/2.0))) + clog.Infof(ctx, "Checking if the session refresh is needed, numSess=%v, refreshThreshold=%v", numSess, refreshThreshold) + if numSess < refreshThreshold { go m.refreshSessions(ctx) } return (numSess > 0 || len(sp.lastSess) > 0) @@ -445,6 +450,9 @@ func (bsm *BroadcastSessionsManager) shouldSkipVerification(sessions []*Broadcas } func NewSessionManager(ctx context.Context, node *core.LivepeerNode, params *core.StreamParameters, sel BroadcastSessionsSelectorFactory) *BroadcastSessionsManager { + if node.Capabilities != nil { + params.Capabilities.SetMinVersionConstraint(node.Capabilities.MinVersionConstraint()) + } var trustedPoolSize, untrustedPoolSize float64 if node.OrchestratorPool != nil { trustedPoolSize = float64(node.OrchestratorPool.SizeWith(common.ScoreAtLeast(common.Score_Trusted))) diff --git a/server/handlers.go b/server/handlers.go index 23992a165..e95315654 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -23,6 +23,7 @@ import ( "github.com/livepeer/go-livepeer/core" "github.com/livepeer/go-livepeer/eth" "github.com/livepeer/go-livepeer/eth/types" + "github.com/livepeer/go-livepeer/monitor" "github.com/livepeer/go-livepeer/pm" "github.com/livepeer/lpms/ffmpeg" "github.com/pkg/errors" @@ -31,6 +32,12 @@ import ( const MainnetChainId = 1 const RinkebyChainId = 4 +func (s *LivepeerServer) healthzHandler() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + respondOk(w, nil) + }) +} + // Status func (s *LivepeerServer) statusHandler() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -128,6 +135,7 @@ func setBroadcastConfigHandler() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { pricePerUnit := r.FormValue("maxPricePerUnit") pixelsPerUnit := r.FormValue("pixelsPerUnit") + currency := r.FormValue("currency") transcodingOptions := r.FormValue("transcodingOptions") if (pricePerUnit == "" || pixelsPerUnit == "") && transcodingOptions == "" { @@ -137,28 +145,38 @@ func setBroadcastConfigHandler() http.Handler { // set max price if pricePerUnit != "" && pixelsPerUnit != "" { - pr, err := strconv.ParseInt(pricePerUnit, 10, 64) - if err != nil { - respond400(w, errors.Wrapf(err, "Error converting string to int64").Error()) + pr, ok := new(big.Rat).SetString(pricePerUnit) + if !ok { + respond400(w, fmt.Sprintf("Error parsing pricePerUnit value: %s", pricePerUnit)) return } - px, err := strconv.ParseInt(pixelsPerUnit, 10, 64) - if err != nil { - respond400(w, errors.Wrapf(err, "Error converting string to int64").Error()) + px, ok := new(big.Rat).SetString(pixelsPerUnit) + if !ok { + respond400(w, fmt.Sprintf("Error parsing pixelsPerUnit value: %s", pixelsPerUnit)) return } - if px <= 0 { - respond400(w, fmt.Sprintf("pixels per unit must be greater than 0, provided %d", px)) + if px.Sign() <= 0 { + respond400(w, fmt.Sprintf("pixels per unit must be greater than 0, provided %v", pixelsPerUnit)) return } - - var price *big.Rat - if pr > 0 { - price = big.NewRat(pr, px) + pricePerPixel := new(big.Rat).Quo(pr, px) + + var autoPrice *core.AutoConvertedPrice + if pricePerPixel.Sign() > 0 { + var err error + autoPrice, err = core.NewAutoConvertedPrice(currency, pricePerPixel, func(price *big.Rat) { + if monitor.Enabled { + monitor.MaxTranscodingPrice(price) + } + glog.Infof("Maximum transcoding price: %v wei per pixel\n", price.FloatString(3)) + }) + if err != nil { + respond400(w, errors.Wrap(err, "error converting price").Error()) + return + } } - BroadcastCfg.SetMaxPrice(price) - glog.Infof("Maximum transcoding price: %d per %q pixels\n", pr, px) + BroadcastCfg.SetMaxPrice(autoPrice) } // set broadcast profiles @@ -294,7 +312,8 @@ func (s *LivepeerServer) activateOrchestratorHandler(client eth.LivepeerEthClien return } - if err := s.setOrchestratorPriceInfo("default", r.FormValue("pricePerUnit"), r.FormValue("pixelsPerUnit")); err != nil { + pricePerUnit, pixelsPerUnit, currency := r.FormValue("pricePerUnit"), r.FormValue("pixelsPerUnit"), r.FormValue("currency") + if err := s.setOrchestratorPriceInfo("default", pricePerUnit, pixelsPerUnit, currency); err != nil { respond400(w, err.Error()) return } @@ -388,8 +407,9 @@ func (s *LivepeerServer) setOrchestratorConfigHandler(client eth.LivepeerEthClie return mustHaveClient(client, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { pixels := r.FormValue("pixelsPerUnit") price := r.FormValue("pricePerUnit") + currency := r.FormValue("currency") if pixels != "" && price != "" { - if err := s.setOrchestratorPriceInfo("default", price, pixels); err != nil { + if err := s.setOrchestratorPriceInfo("default", price, pixels, currency); err != nil { respond400(w, err.Error()) return } @@ -461,53 +481,43 @@ func (s *LivepeerServer) setOrchestratorConfigHandler(client eth.LivepeerEthClie })) } -func (s *LivepeerServer) setOrchestratorPriceInfo(broadcasterEthAddr, pricePerUnitStr, pixelsPerUnitStr string) error { - ok, err := regexp.MatchString("^[0-9]+$", pricePerUnitStr) +func (s *LivepeerServer) setOrchestratorPriceInfo(broadcasterEthAddr, pricePerUnitStr, pixelsPerUnitStr, currency string) error { + ok, err := regexp.MatchString("^0x[0-9a-fA-F]{40}|default$", broadcasterEthAddr) if err != nil { return err } if !ok { - return fmt.Errorf("pricePerUnit is not a valid integer, provided %v", pricePerUnitStr) + return fmt.Errorf("broadcasterEthAddr is not a valid eth address, provided %v", broadcasterEthAddr) } - ok, err = regexp.MatchString("^[0-9]+$", pixelsPerUnitStr) - if err != nil { - return err - } + pricePerUnit, ok := new(big.Rat).SetString(pricePerUnitStr) if !ok { - return fmt.Errorf("pixelsPerUnit is not a valid integer, provided %v", pixelsPerUnitStr) - } - - ok, err = regexp.MatchString("^0x[0-9a-fA-F]{40}|default$", broadcasterEthAddr) - if err != nil { - return err + return fmt.Errorf("error parsing pricePerUnit value: %s", pricePerUnitStr) } - if !ok { - return fmt.Errorf("broadcasterEthAddr is not a valid eth address, provided %v", broadcasterEthAddr) + if pricePerUnit.Sign() < 0 { + return fmt.Errorf("price unit must be greater than or equal to 0, provided %s", pricePerUnitStr) } - pricePerUnit, err := strconv.ParseInt(pricePerUnitStr, 10, 64) - if err != nil { - return fmt.Errorf("error converting pricePerUnit string to int64: %v", err) + pixelsPerUnit, ok := new(big.Rat).SetString(pixelsPerUnitStr) + if !ok { + return fmt.Errorf("error parsing pixelsPerUnit value: %v", pixelsPerUnitStr) } - if pricePerUnit < 0 { - return fmt.Errorf("price unit must be greater than or equal to 0, provided %d", pricePerUnit) + if pixelsPerUnit.Sign() <= 0 { + return fmt.Errorf("pixels per unit must be greater than 0, provided %s", pixelsPerUnitStr) } - pixelsPerUnit, err := strconv.ParseInt(pixelsPerUnitStr, 10, 64) + pricePerPixel := new(big.Rat).Quo(pricePerUnit, pixelsPerUnit) + autoPrice, err := core.NewAutoConvertedPrice(currency, pricePerPixel, func(price *big.Rat) { + if broadcasterEthAddr == "default" { + glog.Infof("Price: %v wei per pixel\n ", price.FloatString(3)) + } else { + glog.Infof("Price: %v wei per pixel for broadcaster %v", price.FloatString(3), broadcasterEthAddr) + } + }) if err != nil { - return fmt.Errorf("error converting pixelsPerUnit string to int64: %v", err) - } - if pixelsPerUnit <= 0 { - return fmt.Errorf("pixels per unit must be greater than 0, provided %d", pixelsPerUnit) - } - - s.LivepeerNode.SetBasePrice(broadcasterEthAddr, big.NewRat(pricePerUnit, pixelsPerUnit)) - if broadcasterEthAddr == "default" { - glog.Infof("Price per pixel set to %d wei for %d pixels\n", pricePerUnit, pixelsPerUnit) - } else { - glog.Infof("Price per pixel set to %d wei for %d pixels for broadcaster %s\n", pricePerUnit, pixelsPerUnit, broadcasterEthAddr) + return fmt.Errorf("error converting price: %v", err) } + s.LivepeerNode.SetBasePrice(broadcasterEthAddr, autoPrice) return nil } @@ -567,9 +577,10 @@ func (s *LivepeerServer) setPriceForBroadcaster() http.Handler { if s.LivepeerNode.NodeType == core.OrchestratorNode { pricePerUnitStr := r.FormValue("pricePerUnit") pixelsPerUnitStr := r.FormValue("pixelsPerUnit") + currency := r.FormValue("currency") broadcasterEthAddr := r.FormValue("broadcasterEthAddr") - err := s.setOrchestratorPriceInfo(broadcasterEthAddr, pricePerUnitStr, pixelsPerUnitStr) + err := s.setOrchestratorPriceInfo(broadcasterEthAddr, pricePerUnitStr, pixelsPerUnitStr, currency) if err == nil { respondOk(w, []byte(fmt.Sprintf("Price per pixel set to %s wei for %s pixels for broadcaster %s\n", pricePerUnitStr, pixelsPerUnitStr, broadcasterEthAddr))) } else { diff --git a/server/handlers_test.go b/server/handlers_test.go index aa01fe88c..d191d0a75 100644 --- a/server/handlers_test.go +++ b/server/handlers_test.go @@ -116,7 +116,7 @@ func TestOrchestratorInfoHandler_Success(t *testing.T) { s := &LivepeerServer{LivepeerNode: n} price := big.NewRat(1, 2) - s.LivepeerNode.SetBasePrice("default", price) + s.LivepeerNode.SetBasePrice("default", core.NewFixedPrice(price)) trans := &types.Transcoder{ ServiceURI: "127.0.0.1:8935", @@ -196,7 +196,7 @@ func TestSetBroadcastConfigHandler_ConvertPricePerUnitError(t *testing.T) { }) assert.Equal(http.StatusBadRequest, status) - assert.Contains(body, "Error converting string to int64") + assert.Contains(body, "Error parsing pricePerUnit value") } func TestSetBroadcastConfigHandler_ConvertPixelsPerUnitError(t *testing.T) { @@ -209,7 +209,7 @@ func TestSetBroadcastConfigHandler_ConvertPixelsPerUnitError(t *testing.T) { }) assert.Equal(http.StatusBadRequest, status) - assert.Contains(body, "Error converting string to int64") + assert.Contains(body, "Error parsing pixelsPerUnit value") } func TestSetBroadcastConfigHandler_NegativePixelPerUnitError(t *testing.T) { @@ -259,7 +259,7 @@ func TestSetBroadcastConfigHandler_Success(t *testing.T) { func TestGetBroadcastConfigHandler(t *testing.T) { assert := assert.New(t) - BroadcastCfg.maxPrice = big.NewRat(1, 2) + BroadcastCfg.maxPrice = core.NewFixedPrice(big.NewRat(1, 2)) BroadcastJobVideoProfiles = []ffmpeg.VideoProfile{ ffmpeg.VideoProfileLookup["P240p25fps16x9"], } @@ -501,27 +501,32 @@ func TestSetOrchestratorPriceInfo(t *testing.T) { s := stubServer() // pricePerUnit is not an integer - err := s.setOrchestratorPriceInfo("default", "nil", "1") + err := s.setOrchestratorPriceInfo("default", "nil", "1", "") assert.Error(t, err) - assert.True(t, strings.Contains(err.Error(), "pricePerUnit is not a valid integer")) + assert.Contains(t, err.Error(), "error parsing pricePerUnit value") // pixelsPerUnit is not an integer - err = s.setOrchestratorPriceInfo("default", "1", "nil") + err = s.setOrchestratorPriceInfo("default", "1", "nil", "") assert.Error(t, err) - assert.True(t, strings.Contains(err.Error(), "pixelsPerUnit is not a valid integer")) + assert.Contains(t, err.Error(), "error parsing pixelsPerUnit value") - err = s.setOrchestratorPriceInfo("default", "1", "1") + // price feed watcher is not initialized and one attempts a custom currency + err = s.setOrchestratorPriceInfo("default", "1e12", "0.7", "USD") + assert.Error(t, err) + assert.Contains(t, err.Error(), "PriceFeedWatcher is not initialized") + + err = s.setOrchestratorPriceInfo("default", "1", "1", "") assert.Nil(t, err) assert.Zero(t, s.LivepeerNode.GetBasePrice("default").Cmp(big.NewRat(1, 1))) - err = s.setOrchestratorPriceInfo("default", "-5", "1") - assert.EqualErrorf(t, err, err.Error(), "price unit must be greater than or equal to 0, provided %d\n", -5) + err = s.setOrchestratorPriceInfo("default", "-5", "1", "") + assert.EqualError(t, err, fmt.Sprintf("price unit must be greater than or equal to 0, provided %d", -5)) // pixels per unit <= 0 - err = s.setOrchestratorPriceInfo("default", "1", "0") - assert.EqualErrorf(t, err, err.Error(), "pixels per unit must be greater than 0, provided %d\n", 0) - err = s.setOrchestratorPriceInfo("default", "1", "-5") - assert.EqualErrorf(t, err, err.Error(), "pixels per unit must be greater than 0, provided %d\n", -5) + err = s.setOrchestratorPriceInfo("default", "1", "0", "") + assert.EqualError(t, err, fmt.Sprintf("pixels per unit must be greater than 0, provided %d", 0)) + err = s.setOrchestratorPriceInfo("default", "1", "-5", "") + assert.EqualError(t, err, fmt.Sprintf("pixels per unit must be greater than 0, provided %d", -5)) } func TestSetPriceForBroadcasterHandler(t *testing.T) { diff --git a/server/mediaserver.go b/server/mediaserver.go index 72a445ffb..1e1b1be15 100644 --- a/server/mediaserver.go +++ b/server/mediaserver.go @@ -44,12 +44,15 @@ import ( "github.com/patrickmn/go-cache" ) -var errAlreadyExists = errors.New("StreamAlreadyExists") -var errStorage = errors.New("ErrStorage") -var errDiscovery = errors.New("ErrDiscovery") -var errNoOrchs = errors.New("ErrNoOrchs") -var errUnknownStream = errors.New("ErrUnknownStream") -var errMismatchedParams = errors.New("Mismatched type for stream params") +var ( + errAlreadyExists = errors.New("StreamAlreadyExists") + errStorage = errors.New("ErrStorage") + errDiscovery = errors.New("ErrDiscovery") + errNoOrchs = errors.New("ErrNoOrchs") + errUnknownStream = errors.New("ErrUnknownStream") + errMismatchedParams = errors.New("Mismatched type for stream params") + errForbidden = errors.New("authentication denied") +) const HLSWaitInterval = time.Second const HLSBufferCap = uint(43200) //12 hrs assuming 1s segment @@ -203,6 +206,9 @@ func (s *LivepeerServer) StartMediaServer(ctx context.Context, httpAddr string) // Store ctx to later use as cancel signal for watchdog goroutine s.context = ctx + // health endpoint + s.HTTPMux.Handle("/healthz", s.healthzHandler()) + //LPMS handlers for handling RTMP video s.LPMS.HandleRTMPPublish(createRTMPStreamIDHandler(ctx, s, nil), gotRTMPStreamHandler(s), endRTMPStreamHandler(s)) s.LPMS.HandleRTMPPlay(getRTMPStreamHandler(s)) @@ -240,8 +246,8 @@ func (s *LivepeerServer) StartMediaServer(ctx context.Context, httpAddr string) } // RTMP Publish Handlers -func createRTMPStreamIDHandler(_ctx context.Context, s *LivepeerServer, webhookResponseOverride *authWebhookResponse) func(url *url.URL) (strmID stream.AppData) { - return func(url *url.URL) (strmID stream.AppData) { +func createRTMPStreamIDHandler(_ctx context.Context, s *LivepeerServer, webhookResponseOverride *authWebhookResponse) func(url *url.URL) (strmID stream.AppData, e error) { + return func(url *url.URL) (strmID stream.AppData, e error) { //Check HTTP header for ManifestID //If ManifestID is passed in HTTP header, use that one //Else check webhook for ManifestID @@ -263,8 +269,8 @@ func createRTMPStreamIDHandler(_ctx context.Context, s *LivepeerServer, webhookR // do not replace captured _ctx variable ctx := clog.AddNonce(_ctx, nonce) if resp, err = authenticateStream(AuthWebhookURL, url.String()); err != nil { - clog.Errorf(ctx, "Authentication denied for streamID url=%s err=%q", url.String(), err) - return nil + clog.Errorf(ctx, fmt.Sprintf("Forbidden: Authentication denied for streamID url=%s err=%q", url.String(), err)) + return nil, errForbidden } // If we've received auth in header AND callback URL forms then for now, we reject cases where they're @@ -272,7 +278,7 @@ func createRTMPStreamIDHandler(_ctx context.Context, s *LivepeerServer, webhookR if resp != nil && webhookResponseOverride != nil { if !resp.areProfilesEqual(*webhookResponseOverride) { clog.Errorf(ctx, "Received auth header with profiles that don't match those in callback URL response") - return nil + return nil, fmt.Errorf("Received auth header with profiles that don't match those in callback URL response") } } @@ -294,8 +300,9 @@ func createRTMPStreamIDHandler(_ctx context.Context, s *LivepeerServer, webhookR parsedProfiles, err := ffmpeg.ParseProfilesFromJsonProfileArray(resp.Profiles) if err != nil { - clog.Errorf(ctx, "Failed to parse JSON video profile for streamID url=%s err=%q", url.String(), err) - return nil + errMsg := fmt.Sprintf("Failed to parse JSON video profile for streamID url=%s err=%q", url.String(), err) + clog.Errorf(ctx, errMsg) + return nil, fmt.Errorf(errMsg) } profiles = append(profiles, parsedProfiles...) @@ -308,16 +315,18 @@ func createRTMPStreamIDHandler(_ctx context.Context, s *LivepeerServer, webhookR if resp.ObjectStore != "" { os, err = drivers.ParseOSURL(resp.ObjectStore, false) if err != nil { - clog.Errorf(ctx, "Failed to parse object store url for streamID url=%s err=%q", url.String(), err) - return nil + errMsg := fmt.Sprintf("Failed to parse object store url for streamID url=%s err=%q", url.String(), err) + clog.Errorf(ctx, errMsg) + return nil, fmt.Errorf(errMsg) } } // set Recording OS if it was provided if resp.RecordObjectStore != "" { ros, err = drivers.ParseOSURL(resp.RecordObjectStore, true) if err != nil { - clog.Errorf(ctx, "Failed to parse recording object store url for streamID url=%s err=%q", url.String(), err) - return nil + errMsg := fmt.Sprintf("Failed to parse recording object store url for streamID url=%s err=%q", url.String(), err) + clog.Errorf(ctx, errMsg) + return nil, fmt.Errorf(errMsg) } } @@ -353,8 +362,10 @@ func createRTMPStreamIDHandler(_ctx context.Context, s *LivepeerServer, webhookR s.connectionLock.RLock() defer s.connectionLock.RUnlock() if core.MaxSessions > 0 && len(s.rtmpConnections) >= core.MaxSessions { - clog.Errorf(ctx, "Too many connections for streamID url=%s err=%q", url.String(), err) - return nil + errMsg := fmt.Sprintf("Too many connections for streamID url=%s err=%q", url.String(), err) + clog.Errorf(ctx, errMsg) + return nil, fmt.Errorf(errMsg) + } return &core.StreamParameters{ ManifestID: mid, @@ -367,7 +378,7 @@ func createRTMPStreamIDHandler(_ctx context.Context, s *LivepeerServer, webhookR RecordOS: ross, VerificationFreq: VerificationFreq, Nonce: nonce, - } + }, nil } } @@ -440,6 +451,7 @@ func gotRTMPStreamHandler(s *LivepeerServer) func(url *url.URL, rtmpStrm stream. func endRTMPStreamHandler(s *LivepeerServer) func(url *url.URL, rtmpStrm stream.RTMPVideoStream) error { return func(url *url.URL, rtmpStrm stream.RTMPVideoStream) error { params := streamParams(rtmpStrm.AppData()) + params.Capabilities.SetMinVersionConstraint(s.LivepeerNode.Capabilities.MinVersionConstraint()) if params == nil { return errMismatchedParams } @@ -826,10 +838,15 @@ func (s *LivepeerServer) HandlePush(w http.ResponseWriter, r *http.Request) { // Check for presence and register if a fresh cxn if !exists { - appData := (createRTMPStreamIDHandler(ctx, s, authHeaderConfig))(r.URL) - if appData == nil { - errorOut(http.StatusInternalServerError, "Could not create stream ID: url=%s", r.URL) - return + appData, err := (createRTMPStreamIDHandler(ctx, s, authHeaderConfig))(r.URL) + if err != nil { + if errors.Is(err, errForbidden) { + errorOut(http.StatusForbidden, "Could not create stream ID: url=%s", r.URL) + return + } else { + errorOut(http.StatusInternalServerError, "Could not create stream ID: url=%s", r.URL) + return + } } params := streamParams(appData) if authHeaderConfig != nil { diff --git a/server/mediaserver_test.go b/server/mediaserver_test.go index 79b4f611f..595c4e29a 100644 --- a/server/mediaserver_test.go +++ b/server/mediaserver_test.go @@ -438,7 +438,9 @@ func TestCreateRTMPStreamHandlerCap(t *testing.T) { oldMaxSessions := core.MaxSessions core.MaxSessions = 1 // happy case - sid := createSid(u).(*core.StreamParameters) + id, err := createSid(u) + require.NoError(t, err) + sid := id.(*core.StreamParameters) mid := sid.ManifestID if mid != "id1" { t.Error("Stream should be allowd", sid) @@ -448,7 +450,8 @@ func TestCreateRTMPStreamHandlerCap(t *testing.T) { } s.rtmpConnections[core.ManifestID("id1")] = nil // capped case - params := createSid(u) + params, err := createSid(u) + require.Error(t, err) if params != nil { t.Error("Stream should be denied because of capacity cap") } @@ -460,7 +463,7 @@ type authWebhookReq struct { } func TestCreateRTMPStreamHandlerWebhook(t *testing.T) { - assert := assert.New(t) + assert := require.New(t) s, cancel := setupServerWithCancel() defer serverCleanup(s) defer cancel() @@ -469,7 +472,8 @@ func TestCreateRTMPStreamHandlerWebhook(t *testing.T) { AuthWebhookURL = mustParseUrl(t, "http://localhost:8938/notexisting") u := mustParseUrl(t, "http://hot/something/id1") - sid := createSid(u) + sid, err := createSid(u) + assert.Error(err) assert.Nil(sid, "Webhook auth failed") ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -486,7 +490,8 @@ func TestCreateRTMPStreamHandlerWebhook(t *testing.T) { })) defer ts.Close() AuthWebhookURL = mustParseUrl(t, ts.URL) - sid = createSid(u) + sid, err = createSid(u) + assert.NoError(err) assert.NotNil(sid, "On empty response with 200 code should pass") // local helper to reduce boilerplate @@ -503,19 +508,23 @@ func TestCreateRTMPStreamHandlerWebhook(t *testing.T) { // empty manifestID ts2 := makeServer(`{"manifestID":""}`) defer ts2.Close() - sid = createSid(u) + sid, err = createSid(u) + assert.Error(err) assert.Nil(sid, "Should not pass if returned manifest id is empty") // invalid json ts3 := makeServer(`{manifestID:"XX"}`) defer ts3.Close() - sid = createSid(u) + sid, err = createSid(u) + assert.Error(err) assert.Nil(sid, "Should not pass if returned json is invalid") // set manifestID ts4 := makeServer(`{"manifestID":"xy"}`) defer ts4.Close() - params := createSid(u).(*core.StreamParameters) + p, err := createSid(u) + assert.NoError(err) + params := p.(*core.StreamParameters) mid := params.ManifestID assert.Equal(core.ManifestID("xy"), mid, "Should set manifest id to one provided by webhook") @@ -526,7 +535,9 @@ func TestCreateRTMPStreamHandlerWebhook(t *testing.T) { // set manifestID + streamKey ts5 := makeServer(`{"manifestID":"xyz", "streamKey":"zyx"}`) defer ts5.Close() - params = createSid(u).(*core.StreamParameters) + id, err := createSid(u) + require.NoError(t, err) + params = id.(*core.StreamParameters) mid = params.ManifestID assert.Equal(core.ManifestID("xyz"), mid, "Should set manifest to one provided by webhook") assert.Equal("xyz/zyx", params.StreamID(), "Should set streamkey to one provided by webhook") @@ -535,7 +546,9 @@ func TestCreateRTMPStreamHandlerWebhook(t *testing.T) { // set presets (with some invalid) ts6 := makeServer(`{"manifestID":"a", "presets":["P240p30fps16x9", "unknown", "P720p30fps16x9"]}`) defer ts6.Close() - params = createSid(u).(*core.StreamParameters) + strmID, err := createSid(u) + require.NoError(t, err) + params = strmID.(*core.StreamParameters) assert.Len(params.Profiles, 2) assert.Equal(params.Profiles, []ffmpeg.VideoProfile{ffmpeg.P240p30fps16x9, ffmpeg.P720p30fps16x9}, "Did not have matching presets") @@ -547,7 +560,9 @@ func TestCreateRTMPStreamHandlerWebhook(t *testing.T) { {"name": "passthru_fps", "bitrate": 890, "width": 789, "height": 654, "profile": "H264ConstrainedHigh", "gop":"123"}, {"name": "gop0", "bitrate": 800, "width": 400, "height": 220, "profile": "H264ConstrainedHigh", "gop":"0.0"}]}`) defer ts7.Close() - params = createSid(u).(*core.StreamParameters) + data, err := createSid(u) + require.NoError(t, err) + params = data.(*core.StreamParameters) assert.Len(params.Profiles, 4) expectedProfiles := []ffmpeg.VideoProfile{ @@ -597,7 +612,9 @@ func TestCreateRTMPStreamHandlerWebhook(t *testing.T) { {"name": "prof1", "bitrate": 432, "fps": 560, "width": 123, "height": 456}, {"name": "prof2", "bitrate": 765, "fps": 876, "width": 456, "height": "hello"}]}`) defer ts8.Close() - params, ok := createSid(u).(*core.StreamParameters) + appData, err := createSid(u) + require.Error(t, err) + params, ok := appData.(*core.StreamParameters) assert.False(ok) assert.Nil(params) @@ -609,7 +626,9 @@ func TestCreateRTMPStreamHandlerWebhook(t *testing.T) { {"name": "gop0", "bitrate": 800, "width": 400, "height": 220, "profile": "H264ConstrainedHigh", "gop":"0.0"}]}`) defer ts9.Close() - params = createSid(u).(*core.StreamParameters) + i, err := createSid(u) + require.NoError(t, err) + params = i.(*core.StreamParameters) jointProfiles := append([]ffmpeg.VideoProfile{ffmpeg.P240p30fps16x9, ffmpeg.P720p30fps16x9}, expectedProfiles...) assert.Len(params.Profiles, 6) @@ -618,7 +637,9 @@ func TestCreateRTMPStreamHandlerWebhook(t *testing.T) { // all invalid presets in webhook should lead to empty set ts10 := makeServer(`{"manifestID":"a", "presets":["very", "unknown"]}`) defer ts10.Close() - params = createSid(u).(*core.StreamParameters) + id2, err := createSid(u) + require.NoError(t, err) + params = id2.(*core.StreamParameters) assert.Len(params.Profiles, 0, "Unexpected value in presets") // invalid gops @@ -636,25 +657,31 @@ func TestCreateRTMPStreamHandlerWebhook(t *testing.T) { // intra only gop ts14 := makeServer(`{"manifestID":"a", "profiles": [ {"gop": "intra" }]}`) defer ts14.Close() - params = createSid(u).(*core.StreamParameters) + id3, err := createSid(u) + require.NoError(t, err) + params = id3.(*core.StreamParameters) assert.Len(params.Profiles, 1) assert.Equal(ffmpeg.GOPIntraOnly, params.Profiles[0].GOP) // do not create stream if ObjectStore URL is invalid ts15 := makeServer(`{"manifestID":"a2", "objectStore": "invalid://object.store", "recordObjectStore": ""}`) defer ts15.Close() - sid = createSid(u) + sid, err = createSid(u) + require.Error(t, err) assert.Nil(sid) // do not create stream if RecordObjectStore URL is invalid ts16 := makeServer(`{"manifestID":"a2", "objectStore": "", "recordObjectStore": "invalid://object.store"}`) defer ts16.Close() - sid = createSid(u) + sid, err = createSid(u) + require.Error(t, err) assert.Nil(sid) ts17 := makeServer(`{"manifestID":"a3", "objectStore": "s3+http://us:pass@object.store/path", "recordObjectStore": "s3+http://us:pass@record.store"}`) defer ts17.Close() - params = createSid(u).(*core.StreamParameters) + id4, err := createSid(u) + require.NoError(t, err) + params = id4.(*core.StreamParameters) assert.Equal(core.ManifestID("a3"), params.ManifestID) assert.NotNil(params.OS) assert.True(params.OS.IsExternal()) @@ -689,30 +716,41 @@ func TestCreateRTMPStreamHandler(t *testing.T) { u := mustParseUrl(t, "rtmp://localhost/"+expectedSid.String()) // with key rand.Seed(123) - sid := createSid(u) + sid, err := createSid(u) + require.NoError(t, err) sap := sid.(*core.StreamParameters) assert.Equal(t, uint64(0x4a68998bed5c40f1), sap.Nonce) - if sid := createSid(u); sid.StreamID() != expectedSid.String() { + sid, err = createSid(u) + require.NoError(t, err) + if sid.StreamID() != expectedSid.String() { t.Error("Unexpected streamid", sid.StreamID()) } u = mustParseUrl(t, "rtmp://localhost/stream/"+expectedSid.String()) // with stream - if sid := createSid(u); sid.StreamID() != expectedSid.String() { + sid, err = createSid(u) + require.NoError(t, err) + if sid.StreamID() != expectedSid.String() { t.Error("Unexpected streamid") } expectedMid := "mnopq" key := common.RandomIDGenerator(StreamKeyBytes) u = mustParseUrl(t, "rtmp://localhost/"+string(expectedMid)) // without key - if sid := createSid(u); sid.StreamID() != string(expectedMid)+"/"+key { + sid, err = createSid(u) + require.NoError(t, err) + if sid.StreamID() != string(expectedMid)+"/"+key { t.Error("Unexpected streamid", sid.StreamID()) } u = mustParseUrl(t, "rtmp://localhost/stream/"+string(expectedMid)) // with stream, without key - if sid := createSid(u); sid.StreamID() != string(expectedMid)+"/"+key { + sid, err = createSid(u) + require.NoError(t, err) + if sid.StreamID() != string(expectedMid)+"/"+key { t.Error("Unexpected streamid", sid.StreamID()) } // Test normal case u = mustParseUrl(t, "rtmp://localhost") - st := stream.NewBasicRTMPVideoStream(createSid(u)) + id, err := createSid(u) + require.NoError(t, err) + st := stream.NewBasicRTMPVideoStream(id) if st.GetStreamID() == "" { t.Error("Empty streamid") } @@ -721,14 +759,18 @@ func TestCreateRTMPStreamHandler(t *testing.T) { t.Error("Handler failed ", err) } // Test collisions via stream reuse - if sid := createSid(u); sid == nil { + sid, err = createSid(u) + require.NoError(t, err) + if sid == nil { t.Error("Did not expect a failure due to naming collision") } // Ensure the stream ID is reusable after the stream ends if err := endHandler(u, st); err != nil { t.Error("Could not clean up stream") } - if sid := createSid(u); sid.StreamID() != st.GetStreamID() { + sid, err = createSid(u) + require.NoError(t, err) + if sid.StreamID() != st.GetStreamID() { t.Error("Mismatched streamid during stream reuse", sid.StreamID(), st.GetStreamID()) } @@ -739,7 +781,9 @@ func TestCreateRTMPStreamHandler(t *testing.T) { // This isn't a great test because if the query param ever changes, // this test will still pass u := mustParseUrl(t, "rtmp://localhost/"+inp) - if sid := createSid(u); sid.StreamID() != st.GetStreamID() { + sid, err = createSid(u) + require.NoError(t, err) + if sid.StreamID() != st.GetStreamID() { t.Errorf("Unexpected StreamID for '%v' ; expected '%v' for input '%v'", sid, st.GetStreamID(), inp) } } @@ -804,7 +848,8 @@ func TestCreateRTMPStreamHandlerWithAuthHeader(t *testing.T) { expectedSid := core.MakeStreamIDFromString("override-manifest-id", "abcdef") u := mustParseUrl(t, "rtmp://localhost/"+expectedSid.String()) // with key - sid := createSid(u) + sid, err := createSid(u) + require.NoError(t, err) require.NotNil(t, sid) require.Equal(t, expectedSid.String(), sid.StreamID()) @@ -874,7 +919,8 @@ func TestCreateRTMPStreamHandlerWithAuthHeader_DifferentProfilesToCallbackURL(t expectedSid := core.MakeStreamIDFromString("override-manifest-id", "abcdef") u := mustParseUrl(t, "rtmp://localhost/"+expectedSid.String()) // with key - sid := createSid(u) + sid, err := createSid(u) + require.Error(t, err) require.Nil(t, sid) } @@ -887,7 +933,8 @@ func TestEndRTMPStreamHandler(t *testing.T) { handler := gotRTMPStreamHandler(s) endHandler := endRTMPStreamHandler(s) u := mustParseUrl(t, "rtmp://localhost") - sid := createSid(u) + sid, err := createSid(u) + require.NoError(t, err) st := stream.NewBasicRTMPVideoStream(sid) // Nonexistent stream @@ -998,7 +1045,9 @@ func TestMultiStream(t *testing.T) { createSid := createRTMPStreamIDHandler(context.TODO(), s, nil) handleStream := func(i int) { - st := stream.NewBasicRTMPVideoStream(createSid(u)) + id, err := createSid(u) + require.NoError(t, err) + st := stream.NewBasicRTMPVideoStream(id) if err := handler(u, st); err != nil { t.Error("Could not handle stream ", i, err) } @@ -1229,7 +1278,8 @@ func TestBroadcastSessionManagerWithStreamStartStop(t *testing.T) { // create BasicRTMPVideoStream and extract ManifestID u := mustParseUrl(t, "rtmp://localhost") - sid := createSid(u) + sid, err := createSid(u) + assert.NoError(err) st := stream.NewBasicRTMPVideoStream(sid) mid := streamParams(st.AppData()).ManifestID @@ -1238,8 +1288,8 @@ func TestBroadcastSessionManagerWithStreamStartStop(t *testing.T) { assert.Equal(exists, false) // assert stream starts successfully - err := handler(u, st) - assert.Nil(err) + err = handler(u, st) + assert.NoError(err) // assert sessManager is running and has right number of sessions cxn, exists := s.rtmpConnections[mid] diff --git a/server/push_test.go b/server/push_test.go index b5d0817e0..bb17ac8a1 100644 --- a/server/push_test.go +++ b/server/push_test.go @@ -1037,7 +1037,7 @@ func TestPush_ForAuthWebhookFailure(t *testing.T) { body, err := ioutil.ReadAll(resp.Body) require.Nil(t, err) - assert.Equal(http.StatusInternalServerError, resp.StatusCode) + assert.Equal(http.StatusForbidden, resp.StatusCode) assert.Contains(strings.TrimSpace(string(body)), "Could not create stream ID") } diff --git a/server/rpc.go b/server/rpc.go index 0fc46494f..0c8b9f806 100644 --- a/server/rpc.go +++ b/server/rpc.go @@ -280,7 +280,7 @@ func GetOrchestratorInfo(ctx context.Context, bcast common.Broadcaster, orchestr return r, nil } -// EndSession - the broadcaster calls EndTranscodingSession to tear down sessions used for verification only once +// EndTranscodingSession - the broadcaster calls EndTranscodingSession to tear down sessions used for verification only once func EndTranscodingSession(ctx context.Context, sess *BroadcastSession) error { uri, err := url.Parse(sess.Transcoder()) if err != nil { @@ -338,7 +338,6 @@ func getOrchestrator(orch Orchestrator, req *net.OrchestratorRequest) (*net.Orch } // currently, orchestrator == transcoder - if req.Capabilities == nil { return orchestratorInfo(orch, addr, orch.ServiceURI().String(), "") } diff --git a/server/rpc_test.go b/server/rpc_test.go index 712568c20..c0832a0c4 100644 --- a/server/rpc_test.go +++ b/server/rpc_test.go @@ -549,13 +549,13 @@ func TestGenPayment(t *testing.T) { sender := &pm.MockSender{} s.Sender = sender - // Test invalid price - BroadcastCfg.SetMaxPrice(big.NewRat(1, 5)) + // Test changing O price + s.InitialPrice = &net.PriceInfo{PricePerUnit: 1, PixelsPerUnit: 7} payment, err = genPayment(context.TODO(), s, 1) assert.Equal("", payment) - assert.Errorf(err, err.Error(), "Orchestrator price higher than the set maximum price of %v wei per %v pixels", int64(1), int64(5)) + assert.Errorf(err, "Orchestrator price has more than doubled, Orchestrator price: %v, Orchestrator initial price: %v", "1/3", "1/7") - BroadcastCfg.SetMaxPrice(nil) + s.InitialPrice = nil // Test CreateTicketBatch error sender.On("CreateTicketBatch", mock.Anything, mock.Anything).Return(nil, errors.New("CreateTicketBatch error")).Once() @@ -680,22 +680,10 @@ func TestValidatePrice(t *testing.T) { PMSessionID: "foo", } - // B's MaxPrice is nil + // O's Initial Price is nil err := validatePrice(s) assert.Nil(err) - defer BroadcastCfg.SetMaxPrice(nil) - - // B MaxPrice > O Price - BroadcastCfg.SetMaxPrice(big.NewRat(5, 1)) - err = validatePrice(s) - assert.Nil(err) - - // B MaxPrice == O Price - BroadcastCfg.SetMaxPrice(big.NewRat(1, 3)) - err = validatePrice(s) - assert.Nil(err) - // O Initial Price == O Price s.InitialPrice = oinfo.PriceInfo err = validatePrice(s) @@ -706,16 +694,15 @@ func TestValidatePrice(t *testing.T) { err = validatePrice(s) assert.Nil(err) - // O Initial Price lower than O Price - s.InitialPrice = &net.PriceInfo{PricePerUnit: 1, PixelsPerUnit: 10} + // O Price higher but up to 2x Initial Price + s.InitialPrice = &net.PriceInfo{PricePerUnit: 1, PixelsPerUnit: 6} err = validatePrice(s) - assert.ErrorContains(err, "price has changed") + assert.Nil(err) - // B MaxPrice < O Price - s.InitialPrice = nil - BroadcastCfg.SetMaxPrice(big.NewRat(1, 5)) + // O Price higher than 2x Initial Price + s.InitialPrice = &net.PriceInfo{PricePerUnit: 1000, PixelsPerUnit: 6001} err = validatePrice(s) - assert.EqualError(err, fmt.Sprintf("Orchestrator price higher than the set maximum price of %v wei per %v pixels", int64(1), int64(5))) + assert.ErrorContains(err, "price has more than doubled") // O.PriceInfo is nil s.OrchestratorInfo.PriceInfo = nil diff --git a/server/segment_rpc.go b/server/segment_rpc.go index 7f81999f7..b7a948b5f 100644 --- a/server/segment_rpc.go +++ b/server/segment_rpc.go @@ -36,6 +36,9 @@ const segmentHeader = "Livepeer-Segment" const pixelEstimateMultiplier = 1.02 +// Maximum price change allowed in orchestrator pricing before the session is swapped. +var priceIncreaseThreshold = big.NewRat(2, 1) + var errSegEncoding = errors.New("ErrorSegEncoding") var errSegSig = errors.New("ErrSegSig") var errFormat = errors.New("unrecognized profile output format") @@ -826,13 +829,18 @@ func validatePrice(sess *BroadcastSession) error { return errors.New("missing orchestrator price") } - maxPrice := BroadcastCfg.MaxPrice() - if maxPrice != nil && oPrice.Cmp(maxPrice) == 1 { - return fmt.Errorf("Orchestrator price higher than the set maximum price of %v wei per %v pixels", maxPrice.Num().Int64(), maxPrice.Denom().Int64()) + initPrice, err := common.RatPriceInfo(sess.InitialPrice) + if err != nil { + glog.Warningf("Error parsing session initial price (%d / %d): %v", + sess.InitialPrice.PricePerUnit, sess.InitialPrice.PixelsPerUnit, err) } - iPrice, err := common.RatPriceInfo(sess.InitialPrice) - if err == nil && iPrice != nil && oPrice.Cmp(iPrice) == 1 { - return fmt.Errorf("Orchestrator price has changed, Orchestrator price: %v, Orchestrator initial price: %v", oPrice, iPrice) + if initPrice != nil { + // Prices are dynamic if configured with a custom currency, so we need to allow some change during the session. + // TODO: Make sure prices stay the same during a session so we can make this logic more strict, disallowing any price changes. + maxIncreasedPrice := new(big.Rat).Mul(initPrice, priceIncreaseThreshold) + if oPrice.Cmp(maxIncreasedPrice) > 0 { + return fmt.Errorf("Orchestrator price has more than doubled, Orchestrator price: %v, Orchestrator initial price: %v", oPrice.RatString(), initPrice.RatString()) + } } return nil diff --git a/server/segment_rpc_test.go b/server/segment_rpc_test.go index 6feb1a5ba..282e3187d 100644 --- a/server/segment_rpc_test.go +++ b/server/segment_rpc_test.go @@ -1677,14 +1677,15 @@ func TestSubmitSegment_GenPaymentError_ValidatePriceError(t *testing.T) { Sender: sender, Balance: balance, OrchestratorInfo: oinfo, + InitialPrice: &net.PriceInfo{ + PricePerUnit: 1, + PixelsPerUnit: 7, + }, } - BroadcastCfg.SetMaxPrice(big.NewRat(1, 5)) - defer BroadcastCfg.SetMaxPrice(nil) - _, err := SubmitSegment(context.TODO(), s, &stream.HLSSegment{}, nil, 0, false, true) - assert.EqualErrorf(t, err, err.Error(), "Orchestrator price higher than the set maximum price of %v wei per %v pixels", int64(1), int64(5)) + assert.EqualError(t, err, fmt.Sprintf("Orchestrator price has more than doubled, Orchestrator price: %v, Orchestrator initial price: %v", "1/3", "1/7")) balance.AssertCalled(t, "Credit", existingCredit) } diff --git a/server/selection.go b/server/selection.go index c7187c79e..c827034db 100644 --- a/server/selection.go +++ b/server/selection.go @@ -3,6 +3,8 @@ package server import ( "container/heap" "context" + "math/big" + ethcommon "github.com/ethereum/go-ethereum/common" "github.com/livepeer/go-livepeer/clog" "github.com/livepeer/go-livepeer/common" @@ -167,7 +169,7 @@ func (s *MinLSSelector) selectUnknownSession(ctx context.Context) *BroadcastSess } var addrs []ethcommon.Address - prices := map[ethcommon.Address]float64{} + prices := map[ethcommon.Address]*big.Rat{} addrCount := make(map[ethcommon.Address]int) for _, sess := range s.unknownSessions { if sess.OrchestratorInfo.GetTicketParams() == nil { @@ -180,9 +182,10 @@ func (s *MinLSSelector) selectUnknownSession(ctx context.Context) *BroadcastSess addrCount[addr]++ pi := sess.OrchestratorInfo.PriceInfo if pi != nil && pi.PixelsPerUnit != 0 { - prices[addr] = float64(pi.PricePerUnit) / float64(pi.PixelsPerUnit) + prices[addr] = big.NewRat(pi.PricePerUnit, pi.PixelsPerUnit) } } + maxPrice := BroadcastCfg.MaxPrice() stakes, err := s.stakeRdr.Stakes(addrs) if err != nil { @@ -199,7 +202,7 @@ func (s *MinLSSelector) selectUnknownSession(ctx context.Context) *BroadcastSess s.perfScore.Mu.Unlock() } - selected := s.selectionAlgorithm.Select(addrs, stakes, prices, perfScores) + selected := s.selectionAlgorithm.Select(ctx, addrs, stakes, maxPrice, prices, perfScores) for i, sess := range s.unknownSessions { if sess.OrchestratorInfo.GetTicketParams() == nil { diff --git a/server/selection_algorithm.go b/server/selection_algorithm.go index 72bc2a663..050b45928 100644 --- a/server/selection_algorithm.go +++ b/server/selection_algorithm.go @@ -1,11 +1,14 @@ package server import ( - ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/golang/glog" + "context" "math" + "math/big" "math/rand" "time" + + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/livepeer/go-livepeer/clog" ) var random = rand.New(rand.NewSource(time.Now().UnixNano())) @@ -20,14 +23,19 @@ type ProbabilitySelectionAlgorithm struct { PriceExpFactor float64 } -func (sa ProbabilitySelectionAlgorithm) Select(addrs []ethcommon.Address, stakes map[ethcommon.Address]int64, prices map[ethcommon.Address]float64, perfScores map[ethcommon.Address]float64) ethcommon.Address { - filtered := sa.filter(addrs, perfScores) +func (sa ProbabilitySelectionAlgorithm) Select(ctx context.Context, addrs []ethcommon.Address, stakes map[ethcommon.Address]int64, maxPrice *big.Rat, prices map[ethcommon.Address]*big.Rat, perfScores map[ethcommon.Address]float64) ethcommon.Address { + filtered := sa.filter(ctx, addrs, maxPrice, prices, perfScores) probabilities := sa.calculateProbabilities(filtered, stakes, prices) return selectBy(probabilities) } -func (sa ProbabilitySelectionAlgorithm) filter(addrs []ethcommon.Address, scores map[ethcommon.Address]float64) []ethcommon.Address { - if sa.MinPerfScore <= 0 || scores == nil || len(scores) == 0 { +func (sa ProbabilitySelectionAlgorithm) filter(ctx context.Context, addrs []ethcommon.Address, maxPrice *big.Rat, prices map[ethcommon.Address]*big.Rat, perfScores map[ethcommon.Address]float64) []ethcommon.Address { + filteredByPerfScore := sa.filterByPerfScore(ctx, addrs, perfScores) + return sa.filterByMaxPrice(ctx, filteredByPerfScore, maxPrice, prices) +} + +func (sa ProbabilitySelectionAlgorithm) filterByPerfScore(ctx context.Context, addrs []ethcommon.Address, scores map[ethcommon.Address]float64) []ethcommon.Address { + if sa.MinPerfScore <= 0 || len(scores) == 0 { // Performance Score filter not defined, return all Orchestrators return addrs } @@ -40,18 +48,41 @@ func (sa ProbabilitySelectionAlgorithm) filter(addrs []ethcommon.Address, scores } if len(res) == 0 { - // If no orchestrators pass the filter, then returns all Orchestrators + // If no orchestrators pass the perf filter, return all Orchestrators. // That may mean some issues with the PerfScore service. - glog.Warning("No Orchestrators passed min performance score filter, not using the filter") + clog.Warningf(ctx, "No Orchestrators passed min performance score filter, not using the filter, numAddrs=%d, minPerfScore=%v, scores=%v, addrs=%v", len(addrs), sa.MinPerfScore, scores, addrs) + return addrs + } + return res +} + +func (sa ProbabilitySelectionAlgorithm) filterByMaxPrice(ctx context.Context, addrs []ethcommon.Address, maxPrice *big.Rat, prices map[ethcommon.Address]*big.Rat) []ethcommon.Address { + if maxPrice == nil || len(prices) == 0 { + // Max price filter not defined, return all Orchestrators + return addrs + } + + var res []ethcommon.Address + for _, addr := range addrs { + price := prices[addr] + if price != nil && price.Cmp(maxPrice) <= 0 { + res = append(res, addr) + } + } + + if len(res) == 0 { + // If no orchestrators pass the filter, return all Orchestrators + // It means that no orchestrators are below the max price + clog.Warningf(ctx, "No Orchestrators passed max price filter, not using the filter, numAddrs=%d, maxPrice=%v, prices=%v, addrs=%v", len(addrs), maxPrice, prices, addrs) return addrs } return res } -func (sa ProbabilitySelectionAlgorithm) calculateProbabilities(addrs []ethcommon.Address, stakes map[ethcommon.Address]int64, prices map[ethcommon.Address]float64) map[ethcommon.Address]float64 { +func (sa ProbabilitySelectionAlgorithm) calculateProbabilities(addrs []ethcommon.Address, stakes map[ethcommon.Address]int64, prices map[ethcommon.Address]*big.Rat) map[ethcommon.Address]float64 { pricesNorm := map[ethcommon.Address]float64{} for _, addr := range addrs { - p := prices[addr] + p, _ := prices[addr].Float64() pricesNorm[addr] = math.Exp(-1 * p / sa.PriceExpFactor) } diff --git a/server/selection_algorithm_test.go b/server/selection_algorithm_test.go index 96ef58f0a..ffae12de3 100644 --- a/server/selection_algorithm_test.go +++ b/server/selection_algorithm_test.go @@ -1,9 +1,12 @@ package server import ( + "context" + "math/big" + "testing" + ethcommon "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" - "testing" ) const testPriceExpFactor = 100 @@ -12,6 +15,8 @@ func TestFilter(t *testing.T) { tests := []struct { name string orchMinPerfScore float64 + maxPrice float64 + prices map[string]float64 orchPerfScores map[string]float64 orchestrators []string want []string @@ -85,21 +90,126 @@ func TestFilter(t *testing.T) { "0x0000000000000000000000000000000000000004", }, }, + { + name: "All prices below max price", + orchMinPerfScore: 0.7, + maxPrice: 2000, + prices: map[string]float64{ + "0x0000000000000000000000000000000000000001": 500, + "0x0000000000000000000000000000000000000002": 1500, + "0x0000000000000000000000000000000000000003": 1000, + }, + orchPerfScores: map[string]float64{ + "0x0000000000000000000000000000000000000001": 0.6, + "0x0000000000000000000000000000000000000002": 0.8, + "0x0000000000000000000000000000000000000003": 0.9, + }, + orchestrators: []string{ + "0x0000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000003", + }, + want: []string{ + "0x0000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000003", + }, + }, + { + name: "All prices above max price", + orchMinPerfScore: 0.7, + maxPrice: 100, + prices: map[string]float64{ + "0x0000000000000000000000000000000000000001": 500, + "0x0000000000000000000000000000000000000002": 1500, + "0x0000000000000000000000000000000000000003": 1000, + }, + orchPerfScores: map[string]float64{ + "0x0000000000000000000000000000000000000001": 0.6, + "0x0000000000000000000000000000000000000002": 0.8, + "0x0000000000000000000000000000000000000003": 0.9, + }, + orchestrators: []string{ + "0x0000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000003", + }, + want: []string{ + "0x0000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000003", + }, + }, + { + name: "Mix of prices relative to max price", + orchMinPerfScore: 0.7, + maxPrice: 750, + prices: map[string]float64{ + "0x0000000000000000000000000000000000000001": 500, + "0x0000000000000000000000000000000000000002": 1500, + "0x0000000000000000000000000000000000000003": 700, + }, + orchPerfScores: map[string]float64{ + "0x0000000000000000000000000000000000000001": 0.8, + "0x0000000000000000000000000000000000000002": 0.6, + "0x0000000000000000000000000000000000000003": 0.9, + }, + orchestrators: []string{ + "0x0000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000003", + }, + want: []string{ + "0x0000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000003", + }, + }, + { + name: "Exact match with max price", + orchMinPerfScore: 0.7, + maxPrice: 1000, + prices: map[string]float64{ + "0x0000000000000000000000000000000000000001": 500, + "0x0000000000000000000000000000000000000002": 1000, // exactly max + "0x0000000000000000000000000000000000000003": 1500, + }, + orchPerfScores: map[string]float64{ + "0x0000000000000000000000000000000000000001": 0.8, + "0x0000000000000000000000000000000000000002": 0.9, + "0x0000000000000000000000000000000000000003": 0.6, + }, + orchestrators: []string{ + "0x0000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000003", + }, + want: []string{ + "0x0000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000002", + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var addrs []ethcommon.Address + var maxPrice *big.Rat + prices := map[ethcommon.Address]*big.Rat{} perfScores := map[ethcommon.Address]float64{} for _, o := range tt.orchestrators { - perfScores[ethcommon.HexToAddress(o)] = tt.orchPerfScores[o] - addrs = append(addrs, ethcommon.HexToAddress(o)) + addr := ethcommon.HexToAddress(o) + addrs = append(addrs, addr) + perfScores[addr] = tt.orchPerfScores[o] + if price, ok := tt.prices[o]; ok { + prices[addr] = new(big.Rat).SetFloat64(price) + } + } + if tt.maxPrice > 0 { + maxPrice = new(big.Rat).SetFloat64(tt.maxPrice) } sa := &ProbabilitySelectionAlgorithm{ MinPerfScore: tt.orchMinPerfScore, } - res := sa.filter(addrs, perfScores) + res := sa.filter(context.Background(), addrs, maxPrice, prices, perfScores) var exp []ethcommon.Address for _, o := range tt.want { @@ -160,13 +270,13 @@ func TestCalculateProbabilities(t *testing.T) { t.Run(tt.name, func(t *testing.T) { var orchs []ethcommon.Address stakes := map[ethcommon.Address]int64{} - prices := map[ethcommon.Address]float64{} + prices := map[ethcommon.Address]*big.Rat{} expProbs := map[ethcommon.Address]float64{} for i, addrStr := range tt.addrs { addr := ethcommon.HexToAddress(addrStr) orchs = append(orchs, addr) stakes[addr] = tt.stakes[i] - prices[addr] = tt.prices[i] + prices[addr] = new(big.Rat).SetFloat64(tt.prices[i]) expProbs[addr] = tt.want[i] } diff --git a/server/selection_test.go b/server/selection_test.go index 716c780d0..aa936ddb0 100644 --- a/server/selection_test.go +++ b/server/selection_test.go @@ -4,10 +4,12 @@ import ( "container/heap" "context" "errors" + "math/big" + "testing" + "github.com/livepeer/go-livepeer/core" "github.com/livepeer/go-livepeer/net" "github.com/stretchr/testify/require" - "testing" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/livepeer/go-livepeer/common" @@ -89,7 +91,7 @@ func (r *stubStakeReader) SetStakes(stakes map[ethcommon.Address]int64) { type stubSelectionAlgorithm struct{} -func (sa stubSelectionAlgorithm) Select(addrs []ethcommon.Address, stakes map[ethcommon.Address]int64, prices map[ethcommon.Address]float64, perfScores map[ethcommon.Address]float64) ethcommon.Address { +func (sa stubSelectionAlgorithm) Select(ctx context.Context, addrs []ethcommon.Address, stakes map[ethcommon.Address]int64, maxPrice *big.Rat, prices map[ethcommon.Address]*big.Rat, perfScores map[ethcommon.Address]float64) ethcommon.Address { if len(addrs) == 0 { return ethcommon.Address{} } @@ -98,7 +100,7 @@ func (sa stubSelectionAlgorithm) Select(addrs []ethcommon.Address, stakes map[et // select lowest price lowest := prices[addr] for _, a := range addrs { - if prices[a] < lowest { + if prices[a].Cmp(lowest) < 0 { addr = a lowest = prices[a] } diff --git a/test/e2e/e2e.go b/test/e2e/e2e.go index 59a77b5c8..efc723d60 100644 --- a/test/e2e/e2e.go +++ b/test/e2e/e2e.go @@ -122,7 +122,7 @@ func lpCfg() starter.LivepeerConfig { ethPassword := "" network := "devnet" blockPollingInterval := 1 - pricePerUnit := 1 + pricePerUnit := "1" initializeRound := true cfg := starter.DefaultLivepeerConfig() diff --git a/tools.go b/tools.go new file mode 100644 index 000000000..6bc191ddd --- /dev/null +++ b/tools.go @@ -0,0 +1,9 @@ +//go:build tools +// +build tools + +package tools + +import ( + _ "github.com/ethereum/go-ethereum/cmd/abigen" + _ "github.com/golang/mock/mockgen" +) From e9b1410f3d89b17b7ba6570669aa98223f4efbd7 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Wed, 7 Aug 2024 23:37:25 +0200 Subject: [PATCH 161/203] Revert "chore: merge master into ai-video (#3103)" (#3123) This reverts commit c2da0bd8bbd70361cd676aacbe08a3b5f6097801. --- .github/workflows/build.yaml | 2 +- .gitignore | 3 - CHANGELOG.md | 130 +- Makefile | 12 +- VERSION | 2 +- cmd/devtool/devtool.go | 2 +- cmd/livepeer/livepeer.go | 15 +- cmd/livepeer/starter/starter.go | 705 ++-- cmd/livepeer/starter/starter_test.go | 92 +- cmd/livepeer_cli/wizard.go | 2 +- cmd/livepeer_cli/wizard_broadcast.go | 22 +- cmd/livepeer_cli/wizard_stats.go | 34 +- cmd/livepeer_cli/wizard_transcoder.go | 49 +- common/types.go | 2 +- core/ai.go | 40 +- core/autoconvertedprice.go | 139 - core/autoconvertedprice_test.go | 258 -- core/capabilities.go | 116 +- core/capabilities_test.go | 187 - core/core_test.go | 4 +- core/livepeernode.go | 40 +- core/livepeernode_test.go | 38 +- core/orch_test.go | 63 +- core/orchestrator.go | 8 +- ..._RoundTrip_Net-20240729130524-3824236.fail | 828 ----- core/transcoder_test.go | 4 +- discovery/db_discovery.go | 14 +- discovery/discovery_test.go | 34 +- .../chainlink/AggregatorV3Interface.abi | 1 - .../chainlink/AggregatorV3Interface.go | 394 --- .../chainlink/AggregatorV3Interface.sol | 36 - eth/pricefeed.go | 78 - eth/pricefeed_test.go | 51 - eth/roundinitializer.go | 95 +- eth/roundinitializer_test.go | 112 +- eth/watchers/pricefeedwatcher.go | 234 -- eth/watchers/pricefeedwatcher_test.go | 249 -- go.mod | 56 +- go.sum | 224 +- install_ffmpeg.sh | 231 +- monitor/census.go | 26 +- net/lp_rpc.pb.go | 3143 ++++++++++------- net/lp_rpc.proto | 13 +- net/lp_rpc_grpc.pb.go | 122 +- net/redeemer.pb.go | 1 + pm/recipient.go | 2 +- pm/sender.go | 2 +- pm/stub.go | 2 +- server/ai_session.go | 7 +- server/broadcast.go | 22 +- server/handlers.go | 109 +- server/handlers_test.go | 35 +- server/mediaserver.go | 65 +- server/mediaserver_test.go | 118 +- server/push_test.go | 2 +- server/rpc.go | 3 +- server/rpc_test.go | 35 +- server/segment_rpc.go | 20 +- server/segment_rpc_test.go | 9 +- server/selection.go | 9 +- server/selection_algorithm.go | 51 +- server/selection_algorithm_test.go | 122 +- server/selection_test.go | 8 +- test/e2e/e2e.go | 2 +- tools.go | 9 - 65 files changed, 3139 insertions(+), 5404 deletions(-) delete mode 100644 core/autoconvertedprice.go delete mode 100644 core/autoconvertedprice_test.go delete mode 100644 core/testdata/rapid/TestCapability_RoundTrip_Net/TestCapability_RoundTrip_Net-20240729130524-3824236.fail delete mode 100644 eth/contracts/chainlink/AggregatorV3Interface.abi delete mode 100644 eth/contracts/chainlink/AggregatorV3Interface.go delete mode 100644 eth/contracts/chainlink/AggregatorV3Interface.sol delete mode 100644 eth/pricefeed.go delete mode 100644 eth/pricefeed_test.go delete mode 100644 eth/watchers/pricefeedwatcher.go delete mode 100644 eth/watchers/pricefeedwatcher_test.go delete mode 100644 tools.go diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index b7b8b5609..022d397b8 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -158,7 +158,7 @@ jobs: target: - GOOS: darwin GOARCH: amd64 - runner: macos-14-large + runner: macos-latest - GOOS: darwin GOARCH: arm64 diff --git a/.gitignore b/.gitignore index 9d493d245..ec5f509ad 100644 --- a/.gitignore +++ b/.gitignore @@ -3,10 +3,7 @@ *.dll *.so *.dylib - -# IDE files *.vscode -*.code-workspace # Test binary, build with `go test -c` *.test diff --git a/CHANGELOG.md b/CHANGELOG.md index fb3c5d803..8bebd6eff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,133 +1,5 @@ # Changelog -## v0.7.6 - -- [#3055](https://github.com/livepeer/go-livepeer/pull/3055) census: Rename broadcaster metrics to gateway metrics -- [#3053](https://github.com/livepeer/go-livepeer/pull/3053) cli: add `-gateway` flag and deprecate `-broadcaster` flag. -- [#3056](https://github.com/livepeer/go-livepeer/pull/3056) cli: add `-pricePerGateway` flag and deprecate `-pricePerBroadcaster` flag. -- [#3060](https://github.com/livepeer/go-livepeer/pull/3060) refactor: rename internal references from Broadcaster to Gateway - -### Breaking Changes 🚨🚨 - -### Features ⚒ - -#### General - -#### Broadcaster - -#### Orchestrator - -#### Transcoder - -### Bug Fixes 🐞 - -#### CLI - -#### General - -#### Broadcaster - -#### Orchestrator - -#### Transcoder - -## v0.7.5 - -### Breaking Changes 🚨🚨 - -### Features ⚒ - -#### General - -- [#3050](https://github.com/livepeer/go-livepeer/pull/3050) Create option to filter Os by min livepeer version used (@leszko) -- [#3029](https://github.com/livepeer/go-livepeer/pull/3029) Initialize round by any B/O who has the initializeRound flag set to true (@leszko) -- [#3040](https://github.com/livepeer/go-livepeer/pull/3040) Fix function names (@kevincatty) - -#### Broadcaster - -- [#2995](https://github.com/livepeer/go-livepeer/pull/2995) server: Allow Os price to increase up to 2x mid-session (@victorges) -- [#2999](https://github.com/livepeer/go-livepeer/pull/2999) server,discovery: Allow B to use any O in case none match maxPrice (@victorges) - -#### Orchestrator - -#### Transcoder - -### Bug Fixes 🐞 - -#### CLI - -#### General - -#### Broadcaster - -- [#2994](https://github.com/livepeer/go-livepeer/pull/2994) server: Skip redundant maxPrice check in ongoing session (@victorges) - -#### Orchestrator - -- [#3001](https://github.com/livepeer/go-livepeer/pull/3001) Fix transcoding price metrics (@leszko) - -#### Transcoder - -- [#3003](https://github.com/livepeer/go-livepeer/pull/3003) Fix issue in the transcoding layer for WebRTC input (@j0sh) - -## v0.7.4 - -### Breaking Changes 🚨🚨 - -### Features ⚒ - -#### General - -- [#2989](https://github.com/livepeer/go-livepeer/pull/2989) Revert "Update ffmpeg version" (@thomshutt) - -#### Broadcaster - -#### Orchestrator - -#### Transcoder - -### Bug Fixes 🐞 - -#### CLI - -#### General - -#### Broadcaster - -#### Orchestrator - -#### Transcoder - -## v0.7.3 - -### Breaking Changes 🚨🚨 - -### Features ⚒ - -#### General - -- [#2978](https://github.com/livepeer/go-livepeer/pull/2978) Update CUDA version from 11.x to 12.x (@leszko) -- [#2973](https://github.com/livepeer/go-livepeer/pull/2973) Update ffmpeg version (@thomshutt) -- [#2981](https://github.com/livepeer/go-livepeer/pull/2981) Add support for prices in custom currencies like USD (@victorges) - -#### Broadcaster - -#### Orchestrator - -#### Transcoder - -### Bug Fixes 🐞 - -#### CLI - -#### General - -#### Broadcaster - -#### Orchestrator - -#### Transcoder - ## v0.7.2 ### Breaking Changes 🚨🚨 @@ -690,7 +562,7 @@ Additional highlights of this release: - Support for EIP-1559 (otherwise known as type 2) Ethereum transactions which results in more predictable transaction confirmation times, reduces the chance of stuck pending transactions and avoids overpaying in gas fees. If you are interested in additional details on the implications of EIP-1559 transactions refer to this [resource](https://hackmd.io/@timbeiko/1559-resources). - An improvement in ticket parameter generation for orchestrators to prevent short lived gas price spikes on the Ethereum network from disrupting streams. - The node will automatically detect if the GPU enters an unrecoverable state and crash. The reason for crashing upon detecting an unrecoverable GPU state is that no transcoding will - be possible in this scenario until the node is restarted. We recommend node operators to setup a process for monitoring if their node is still up and starting the node if it has crashed. For reference, a bash script similar to [this one](https://gist.github.com/jailuthra/03c3d65d0bbff457cae8f9a14b4c04b7) can be used to automate restarts of the node in the event of a crash. +be possible in this scenario until the node is restarted. We recommend node operators to setup a process for monitoring if their node is still up and starting the node if it has crashed. For reference, a bash script similar to [this one](https://gist.github.com/jailuthra/03c3d65d0bbff457cae8f9a14b4c04b7) can be used to automate restarts of the node in the event of a crash. Thanks to everyone that submitted bug reports and assisted in testing! diff --git a/Makefile b/Makefile index d57d7ffc9..54ca0c6e9 100644 --- a/Makefile +++ b/Makefile @@ -2,9 +2,8 @@ SHELL=/bin/bash GO_BUILD_DIR?="./" MOCKGEN=go run github.com/golang/mock/mockgen -ABIGEN=go run github.com/ethereum/go-ethereum/cmd/abigen -all: net/lp_rpc.pb.go net/redeemer.pb.go net/redeemer_mock.pb.go core/test_segment.go eth/contracts/chainlink/AggregatorV3Interface.go livepeer livepeer_cli livepeer_router livepeer_bench +all: net/lp_rpc.pb.go net/redeemer.pb.go net/redeemer_mock.pb.go core/test_segment.go livepeer livepeer_cli livepeer_router livepeer_bench net/lp_rpc.pb.go: net/lp_rpc.proto protoc -I=. --go_out=. --go-grpc_out=. $^ @@ -19,15 +18,6 @@ net/redeemer_mock.pb.go net/redeemer_grpc_mock.pb.go: net/redeemer.pb.go net/red core/test_segment.go: core/test_segment.sh core/test_segment.go -eth/contracts/chainlink/AggregatorV3Interface.go: - solc --version | grep 0.7.6+commit.7338295f - @set -ex; \ - for sol_file in eth/contracts/chainlink/*.sol; do \ - contract_name=$$(basename "$$sol_file" .sol); \ - solc --abi --optimize --overwrite -o $$(dirname "$$sol_file") $$sol_file; \ - $(ABIGEN) --abi=$${sol_file%.sol}.abi --pkg=chainlink --type=$$contract_name --out=$${sol_file%.sol}.go; \ - done - version=$(shell cat VERSION) ldflags := -X github.com/livepeer/go-livepeer/core.LivepeerVersion=$(shell ./print_version.sh) diff --git a/VERSION b/VERSION index 4d01880a7..d5cc44d1d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.7.6 \ No newline at end of file +0.7.2 \ No newline at end of file diff --git a/cmd/devtool/devtool.go b/cmd/devtool/devtool.go index 61e9774fe..7052ed032 100644 --- a/cmd/devtool/devtool.go +++ b/cmd/devtool/devtool.go @@ -95,7 +95,7 @@ func main() { if !goodToGo { fmt.Println(` Usage: go run cmd/devtool/devtool.go setup broadcaster|transcoder [nodeIndex] - It will create initialize eth account (on private testnet) to be used for broadcaster or transcoder + It will create initilize eth account (on private testnet) to be used for broadcaster or transcoder and will create shell script (run_broadcaster_ETHACC.sh or run_transcoder_ETHACC.sh) to run it. Node index indicates how much to offset node's port. Orchestrator node's index by default is 1. For example: diff --git a/cmd/livepeer/livepeer.go b/cmd/livepeer/livepeer.go index 2653e55a6..99c625985 100755 --- a/cmd/livepeer/livepeer.go +++ b/cmd/livepeer/livepeer.go @@ -127,14 +127,13 @@ func parseLivepeerConfig() starter.LivepeerConfig { cfg.OrchAddr = flag.String("orchAddr", *cfg.OrchAddr, "Comma-separated list of orchestrators to connect to") cfg.OrchWebhookURL = flag.String("orchWebhookUrl", *cfg.OrchWebhookURL, "Orchestrator discovery callback URL") cfg.OrchBlacklist = flag.String("orchBlocklist", "", "Comma-separated list of blocklisted orchestrators") - cfg.OrchMinLivepeerVersion = flag.String("orchMinLivepeerVersion", *cfg.OrchMinLivepeerVersion, "Minimal go-livepeer version orchestrator should have to be selected") cfg.SelectRandWeight = flag.Float64("selectRandFreq", *cfg.SelectRandWeight, "Weight of the random factor in the orchestrator selection algorithm") cfg.SelectStakeWeight = flag.Float64("selectStakeWeight", *cfg.SelectStakeWeight, "Weight of the stake factor in the orchestrator selection algorithm") cfg.SelectPriceWeight = flag.Float64("selectPriceWeight", *cfg.SelectPriceWeight, "Weight of the price factor in the orchestrator selection algorithm") cfg.SelectPriceExpFactor = flag.Float64("selectPriceExpFactor", *cfg.SelectPriceExpFactor, "Expresses how significant a small change of price is for the selection algorithm; default 100") cfg.OrchPerfStatsURL = flag.String("orchPerfStatsUrl", *cfg.OrchPerfStatsURL, "URL of Orchestrator Performance Stream Tester") cfg.Region = flag.String("region", *cfg.Region, "Region in which a broadcaster is deployed; used to select the region while using the orchestrator's performance stats") - cfg.MaxPricePerUnit = flag.String("maxPricePerUnit", *cfg.MaxPricePerUnit, "The maximum transcoding price per 'pixelsPerUnit' a broadcaster is willing to accept. If not set explicitly, broadcaster is willing to accept ANY price. Can be specified in wei or a custom currency in the format (e.g. 0.50USD). When using a custom currency, a corresponding price feed must be configured with -priceFeedAddr") + cfg.MaxPricePerUnit = flag.Int("maxPricePerUnit", *cfg.MaxPricePerUnit, "The maximum transcoding price (in wei) per 'pixelsPerUnit' a broadcaster is willing to accept. If not set explicitly, broadcaster is willing to accept ANY price") cfg.MinPerfScore = flag.Float64("minPerfScore", *cfg.MinPerfScore, "The minimum orchestrator's performance score a broadcaster is willing to accept") // Transcoding: @@ -170,7 +169,6 @@ func parseLivepeerConfig() starter.LivepeerConfig { cfg.MaxGasPrice = flag.Int("maxGasPrice", *cfg.MaxGasPrice, "Maximum gas price (priority fee + base fee) for ETH transactions in wei, 40 Gwei = 40000000000") cfg.EthController = flag.String("ethController", *cfg.EthController, "Protocol smart contract address") cfg.InitializeRound = flag.Bool("initializeRound", *cfg.InitializeRound, "Set to true if running as a transcoder and the node should automatically initialize new rounds") - cfg.InitializeRoundMaxDelay = flag.Duration("initializeRoundMaxDelay", *cfg.InitializeRoundMaxDelay, "Maximum delay to wait before initializing a round") cfg.TicketEV = flag.String("ticketEV", *cfg.TicketEV, "The expected value for PM tickets") cfg.MaxFaceValue = flag.String("maxFaceValue", *cfg.MaxFaceValue, "set max ticket face value in WEI") // Broadcaster max acceptable ticket EV @@ -180,13 +178,12 @@ func parseLivepeerConfig() starter.LivepeerConfig { // Broadcaster deposit multiplier to determine max acceptable ticket faceValue cfg.DepositMultiplier = flag.Int("depositMultiplier", *cfg.DepositMultiplier, "The deposit multiplier used to determine max acceptable faceValue for PM tickets") // Orchestrator base pricing info - cfg.PricePerUnit = flag.String("pricePerUnit", "0", "The price per 'pixelsPerUnit' amount pixels. Can be specified in wei or a custom currency in the format (e.g. 0.50USD). When using a custom currency, a corresponding price feed must be configured with -priceFeedAddr") - // Unit of pixels for both O's pricePerUnit and B's maxPricePerUnit - cfg.PixelsPerUnit = flag.String("pixelsPerUnit", *cfg.PixelsPerUnit, "Amount of pixels per unit. Set to '> 1' to have smaller price granularity than 1 wei / pixel") - cfg.PriceFeedAddr = flag.String("priceFeedAddr", *cfg.PriceFeedAddr, "ETH address of the Chainlink price feed contract. Used for custom currencies conversion on -pricePerUnit or -maxPricePerUnit") + cfg.PricePerUnit = flag.Int("pricePerUnit", 0, "The price per 'pixelsPerUnit' amount pixels") + // Unit of pixels for both O's basePriceInfo and B's MaxBroadcastPrice + cfg.PixelsPerUnit = flag.Int("pixelsPerUnit", *cfg.PixelsPerUnit, "Amount of pixels per unit. Set to '> 1' to have smaller price granularity than 1 wei / pixel") cfg.AutoAdjustPrice = flag.Bool("autoAdjustPrice", *cfg.AutoAdjustPrice, "Enable/disable automatic price adjustments based on the overhead for redeeming tickets") - cfg.PricePerGateway = flag.String("pricePerGateway", *cfg.PricePerGateway, `json list of price per gateway or path to json config file. Example: {"gateways":[{"ethaddress":"address1","priceperunit":0.5,"currency":"USD","pixelsperunit":1000000000000},{"ethaddress":"address2","priceperunit":0.3,"currency":"USD","pixelsperunit":1000000000000}]}`) - cfg.PricePerBroadcaster = flag.String("pricePerBroadcaster", *cfg.PricePerBroadcaster, `json list of price per broadcaster or path to json config file. Example: {"broadcasters":[{"ethaddress":"address1","priceperunit":0.5,"currency":"USD","pixelsperunit":1000000000000},{"ethaddress":"address2","priceperunit":0.3,"currency":"USD","pixelsperunit":1000000000000}]}`) + cfg.PricePerGateway = flag.String("pricePerGateway", *cfg.PricePerGateway, `json list of price per gateway or path to json config file. Example: {"gateways":[{"ethaddress":"address1","priceperunit":1000,"pixelsperunit":1},{"ethaddress":"address2","priceperunit":1200,"pixelsperunit":1}]}`) + cfg.PricePerBroadcaster = flag.String("pricePerBroadcaster", *cfg.PricePerBroadcaster, `json list of price per broadcaster or path to json config file. Example: {"broadcasters":[{"ethaddress":"address1","priceperunit":1000,"pixelsperunit":1},{"ethaddress":"address2","priceperunit":1200,"pixelsperunit":1}]}`) // Interval to poll for blocks cfg.BlockPollingInterval = flag.Int("blockPollingInterval", *cfg.BlockPollingInterval, "Interval in seconds at which different blockchain event services poll for blocks") // Redemption service diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index 5354820ab..7568f1f13 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -16,7 +16,6 @@ import ( "os/user" "path" "path/filepath" - "regexp" "strconv" "strings" "time" @@ -35,7 +34,6 @@ import ( "github.com/livepeer/go-livepeer/eth" "github.com/livepeer/go-livepeer/eth/blockwatch" "github.com/livepeer/go-livepeer/eth/watchers" - "github.com/livepeer/go-livepeer/monitor" lpmon "github.com/livepeer/go-livepeer/monitor" "github.com/livepeer/go-livepeer/pm" "github.com/livepeer/go-livepeer/server" @@ -78,84 +76,81 @@ const ( ) type LivepeerConfig struct { - Network *string - RtmpAddr *string - CliAddr *string - HttpAddr *string - ServiceAddr *string - OrchAddr *string - VerifierURL *string - EthController *string - VerifierPath *string - LocalVerify *bool - HttpIngest *bool - Orchestrator *bool - Transcoder *bool - AIWorker *bool - Gateway *bool - Broadcaster *bool - OrchSecret *string - TranscodingOptions *string - AIModels *string - MaxAttempts *int - SelectRandWeight *float64 - SelectStakeWeight *float64 - SelectPriceWeight *float64 - SelectPriceExpFactor *float64 - OrchPerfStatsURL *string - Region *string - MaxPricePerUnit *string - MinPerfScore *float64 - MaxSessions *string - CurrentManifest *bool - Nvidia *string - Netint *string - TestTranscoder *bool - EthAcctAddr *string - EthPassword *string - EthKeystorePath *string - EthOrchAddr *string - EthUrl *string - TxTimeout *time.Duration - MaxTxReplacements *int - GasLimit *int - MinGasPrice *int64 - MaxGasPrice *int - InitializeRound *bool - InitializeRoundMaxDelay *time.Duration - TicketEV *string - MaxFaceValue *string - MaxTicketEV *string - MaxTotalEV *string - DepositMultiplier *int - PricePerUnit *string - PixelsPerUnit *string - PriceFeedAddr *string - AutoAdjustPrice *bool - PricePerGateway *string - PricePerBroadcaster *string - BlockPollingInterval *int - Redeemer *bool - RedeemerAddr *string - Reward *bool - Monitor *bool - MetricsPerStream *bool - MetricsExposeClientIP *bool - MetadataQueueUri *string - MetadataAmqpExchange *string - MetadataPublishTimeout *time.Duration - Datadir *string - AIModelsDir *string - Objectstore *string - Recordstore *string - FVfailGsBucket *string - FVfailGsKey *string - AuthWebhookURL *string - OrchWebhookURL *string - OrchBlacklist *string - OrchMinLivepeerVersion *string - TestOrchAvail *bool - AIRunnerImage *string + Network *string + RtmpAddr *string + CliAddr *string + HttpAddr *string + ServiceAddr *string + OrchAddr *string + VerifierURL *string + EthController *string + VerifierPath *string + LocalVerify *bool + HttpIngest *bool + Orchestrator *bool + Transcoder *bool + AIWorker *bool + Gateway *bool + Broadcaster *bool + OrchSecret *string + TranscodingOptions *string + AIModels *string + MaxAttempts *int + SelectRandWeight *float64 + SelectStakeWeight *float64 + SelectPriceWeight *float64 + SelectPriceExpFactor *float64 + OrchPerfStatsURL *string + Region *string + MaxPricePerUnit *int + MinPerfScore *float64 + MaxSessions *string + CurrentManifest *bool + Nvidia *string + Netint *string + TestTranscoder *bool + EthAcctAddr *string + EthPassword *string + EthKeystorePath *string + EthOrchAddr *string + EthUrl *string + TxTimeout *time.Duration + MaxTxReplacements *int + GasLimit *int + MinGasPrice *int64 + MaxGasPrice *int + InitializeRound *bool + TicketEV *string + MaxFaceValue *string + MaxTicketEV *string + MaxTotalEV *string + DepositMultiplier *int + PricePerUnit *int + PixelsPerUnit *int + AutoAdjustPrice *bool + PricePerGateway *string + PricePerBroadcaster *string + BlockPollingInterval *int + Redeemer *bool + RedeemerAddr *string + Reward *bool + Monitor *bool + MetricsPerStream *bool + MetricsExposeClientIP *bool + MetadataQueueUri *string + MetadataAmqpExchange *string + MetadataPublishTimeout *time.Duration + Datadir *string + AIModelsDir *string + Objectstore *string + Recordstore *string + FVfailGsBucket *string + FVfailGsKey *string + AuthWebhookURL *string + OrchWebhookURL *string + OrchBlacklist *string + TestOrchAvail *bool + AIRunnerImage *string } // DefaultLivepeerConfig creates LivepeerConfig exactly the same as when no flags are passed to the livepeer process. @@ -209,15 +204,13 @@ func DefaultLivepeerConfig() LivepeerConfig { defaultMaxGasPrice := 0 defaultEthController := "" defaultInitializeRound := false - defaultInitializeRoundMaxDelay := 30 * time.Second defaultTicketEV := "8000000000" defaultMaxFaceValue := "0" defaultMaxTicketEV := "3000000000000" defaultMaxTotalEV := "20000000000000" defaultDepositMultiplier := 1 - defaultMaxPricePerUnit := "0" - defaultPixelsPerUnit := "1" - defaultPriceFeedAddr := "0x639Fe6ab55C921f74e7fac1ee960C0B6293ba612" // ETH / USD price feed address on Arbitrum Mainnet + defaultMaxPricePerUnit := 0 + defaultPixelsPerUnit := 1 defaultAutoAdjustPrice := true defaultPricePerGateway := "" defaultPricePerBroadcaster := "" @@ -249,7 +242,6 @@ func DefaultLivepeerConfig() LivepeerConfig { // API defaultAuthWebhookURL := "" defaultOrchWebhookURL := "" - defaultMinLivepeerVersion := "" // Flags defaultTestOrchAvail := true @@ -293,38 +285,36 @@ func DefaultLivepeerConfig() LivepeerConfig { AIRunnerImage: &defaultAIRunnerImage, // Onchain: - EthAcctAddr: &defaultEthAcctAddr, - EthPassword: &defaultEthPassword, - EthKeystorePath: &defaultEthKeystorePath, - EthOrchAddr: &defaultEthOrchAddr, - EthUrl: &defaultEthUrl, - TxTimeout: &defaultTxTimeout, - MaxTxReplacements: &defaultMaxTxReplacements, - GasLimit: &defaultGasLimit, - MaxGasPrice: &defaultMaxGasPrice, - EthController: &defaultEthController, - InitializeRound: &defaultInitializeRound, - InitializeRoundMaxDelay: &defaultInitializeRoundMaxDelay, - TicketEV: &defaultTicketEV, - MaxFaceValue: &defaultMaxFaceValue, - MaxTicketEV: &defaultMaxTicketEV, - MaxTotalEV: &defaultMaxTotalEV, - DepositMultiplier: &defaultDepositMultiplier, - MaxPricePerUnit: &defaultMaxPricePerUnit, - PixelsPerUnit: &defaultPixelsPerUnit, - PriceFeedAddr: &defaultPriceFeedAddr, - AutoAdjustPrice: &defaultAutoAdjustPrice, - PricePerGateway: &defaultPricePerGateway, - PricePerBroadcaster: &defaultPricePerBroadcaster, - BlockPollingInterval: &defaultBlockPollingInterval, - Redeemer: &defaultRedeemer, - RedeemerAddr: &defaultRedeemerAddr, - Monitor: &defaultMonitor, - MetricsPerStream: &defaultMetricsPerStream, - MetricsExposeClientIP: &defaultMetricsExposeClientIP, - MetadataQueueUri: &defaultMetadataQueueUri, - MetadataAmqpExchange: &defaultMetadataAmqpExchange, - MetadataPublishTimeout: &defaultMetadataPublishTimeout, + EthAcctAddr: &defaultEthAcctAddr, + EthPassword: &defaultEthPassword, + EthKeystorePath: &defaultEthKeystorePath, + EthOrchAddr: &defaultEthOrchAddr, + EthUrl: &defaultEthUrl, + TxTimeout: &defaultTxTimeout, + MaxTxReplacements: &defaultMaxTxReplacements, + GasLimit: &defaultGasLimit, + MaxGasPrice: &defaultMaxGasPrice, + EthController: &defaultEthController, + InitializeRound: &defaultInitializeRound, + TicketEV: &defaultTicketEV, + MaxFaceValue: &defaultMaxFaceValue, + MaxTicketEV: &defaultMaxTicketEV, + MaxTotalEV: &defaultMaxTotalEV, + DepositMultiplier: &defaultDepositMultiplier, + MaxPricePerUnit: &defaultMaxPricePerUnit, + PixelsPerUnit: &defaultPixelsPerUnit, + AutoAdjustPrice: &defaultAutoAdjustPrice, + PricePerGateway: &defaultPricePerGateway, + PricePerBroadcaster: &defaultPricePerBroadcaster, + BlockPollingInterval: &defaultBlockPollingInterval, + Redeemer: &defaultRedeemer, + RedeemerAddr: &defaultRedeemerAddr, + Monitor: &defaultMonitor, + MetricsPerStream: &defaultMetricsPerStream, + MetricsExposeClientIP: &defaultMetricsExposeClientIP, + MetadataQueueUri: &defaultMetadataQueueUri, + MetadataAmqpExchange: &defaultMetadataAmqpExchange, + MetadataPublishTimeout: &defaultMetadataPublishTimeout, // Ingest: HttpIngest: &defaultHttpIngest, @@ -345,9 +335,6 @@ func DefaultLivepeerConfig() LivepeerConfig { AuthWebhookURL: &defaultAuthWebhookURL, OrchWebhookURL: &defaultOrchWebhookURL, - // Versioning constraints - OrchMinLivepeerVersion: &defaultMinLivepeerVersion, - // Flags TestOrchAvail: &defaultTestOrchAvail, } @@ -517,6 +504,152 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { } } + var aiCaps []core.Capability + constraints := make(map[core.Capability]*core.Constraints) + + if *cfg.AIWorker { + gpus := []string{} + if *cfg.Nvidia != "" { + var err error + gpus, err = common.ParseAccelDevices(*cfg.Nvidia, ffmpeg.Nvidia) + if err != nil { + glog.Errorf("Error parsing -nvidia for devices: %v", err) + return + } + } + + modelsDir := *cfg.AIModelsDir + if modelsDir == "" { + var err error + modelsDir, err = filepath.Abs(path.Join(*cfg.Datadir, "models")) + if err != nil { + glog.Error("Error creating absolute path for models dir: %v", modelsDir) + return + } + } + + if err := os.MkdirAll(modelsDir, 0755); err != nil { + glog.Error("Error creating models dir %v", modelsDir) + return + } + + n.AIWorker, err = worker.NewWorker(*cfg.AIRunnerImage, gpus, modelsDir) + if err != nil { + glog.Errorf("Error starting AI worker: %v", err) + return + } + + if *cfg.AIModels != "" { + configs, err := core.ParseAIModelConfigs(*cfg.AIModels) + if err != nil { + glog.Errorf("Error parsing -aiModels: %v", err) + return + } + + for _, config := range configs { + modelConstraint := &core.ModelConstraint{Warm: config.Warm} + + // If the config contains a URL we call Warm() anyway because AIWorker will just register + // the endpoint for an external container + if config.Warm || config.URL != "" { + endpoint := worker.RunnerEndpoint{URL: config.URL, Token: config.Token} + if err := n.AIWorker.Warm(ctx, config.Pipeline, config.ModelID, endpoint, config.OptimizationFlags); err != nil { + glog.Errorf("Error AI worker warming %v container: %v", config.Pipeline, err) + return + } + } + + // Show warning if people set OptimizationFlags but not Warm. + if len(config.OptimizationFlags) > 0 && !config.Warm { + glog.Warningf("Model %v has 'optimization_flags' set without 'warm'. Optimization flags are currently only used for warm containers.", config.ModelID) + } + + switch config.Pipeline { + case "text-to-image": + _, ok := constraints[core.Capability_TextToImage] + if !ok { + aiCaps = append(aiCaps, core.Capability_TextToImage) + constraints[core.Capability_TextToImage] = &core.Constraints{ + Models: make(map[string]*core.ModelConstraint), + } + } + + constraints[core.Capability_TextToImage].Models[config.ModelID] = modelConstraint + + n.SetBasePriceForCap("default", core.Capability_TextToImage, config.ModelID, big.NewRat(config.PricePerUnit, config.PixelsPerUnit)) + case "image-to-image": + _, ok := constraints[core.Capability_ImageToImage] + if !ok { + aiCaps = append(aiCaps, core.Capability_ImageToImage) + constraints[core.Capability_ImageToImage] = &core.Constraints{ + Models: make(map[string]*core.ModelConstraint), + } + } + + constraints[core.Capability_ImageToImage].Models[config.ModelID] = modelConstraint + + n.SetBasePriceForCap("default", core.Capability_ImageToImage, config.ModelID, big.NewRat(config.PricePerUnit, config.PixelsPerUnit)) + case "image-to-video": + _, ok := constraints[core.Capability_ImageToVideo] + if !ok { + aiCaps = append(aiCaps, core.Capability_ImageToVideo) + constraints[core.Capability_ImageToVideo] = &core.Constraints{ + Models: make(map[string]*core.ModelConstraint), + } + } + + constraints[core.Capability_ImageToVideo].Models[config.ModelID] = modelConstraint + + n.SetBasePriceForCap("default", core.Capability_ImageToVideo, config.ModelID, big.NewRat(config.PricePerUnit, config.PixelsPerUnit)) + case "upscale": + _, ok := constraints[core.Capability_Upscale] + if !ok { + aiCaps = append(aiCaps, core.Capability_Upscale) + constraints[core.Capability_Upscale] = &core.Constraints{ + Models: make(map[string]*core.ModelConstraint), + } + } + + constraints[core.Capability_Upscale].Models[config.ModelID] = modelConstraint + + n.SetBasePriceForCap("default", core.Capability_Upscale, config.ModelID, big.NewRat(config.PricePerUnit, config.PixelsPerUnit)) + case "audio-to-text": + _, ok := constraints[core.Capability_AudioToText] + if !ok { + aiCaps = append(aiCaps, core.Capability_AudioToText) + constraints[core.Capability_AudioToText] = &core.Constraints{ + Models: make(map[string]*core.ModelConstraint), + } + } + + constraints[core.Capability_AudioToText].Models[config.ModelID] = modelConstraint + + n.SetBasePriceForCap("default", core.Capability_AudioToText, config.ModelID, big.NewRat(config.PricePerUnit, config.PixelsPerUnit)) + } + + if len(aiCaps) > 0 { + capability := aiCaps[len(aiCaps)-1] + price := n.GetBasePriceForCap("default", capability, config.ModelID) + glog.V(6).Infof("Capability %s (ID: %v) advertised with model constraint %s at price %d per %d unit", config.Pipeline, capability, config.ModelID, price.Num(), price.Denom()) + } + } + } else { + glog.Error("The '-aiModels' flag was set, but no model configuration was provided. Please specify the model configuration using the '-aiModels' flag.") + return + } + + defer func() { + ctx, cancel := context.WithTimeout(context.Background(), aiWorkerContainerStopTimeout) + defer cancel() + if err := n.AIWorker.Stop(ctx); err != nil { + glog.Errorf("Error stopping AI worker containers: %v", err) + return + } + + glog.Infof("Stopped AI worker containers") + }() + } + if *cfg.Redeemer { n.NodeType = core.RedeemerNode } else if *cfg.Orchestrator { @@ -574,6 +707,7 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { glog.Error(err) return } + } else { n.SelectionAlgorithm, err = createSelectionAlgorithm(cfg) if err != nil { @@ -753,13 +887,6 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { go serviceRegistryWatcher.Watch() defer serviceRegistryWatcher.Stop() - core.PriceFeedWatcher, err = watchers.NewPriceFeedWatcher(backend, *cfg.PriceFeedAddr) - // The price feed watch loop is started on demand on first subscribe. - if err != nil { - glog.Errorf("Failed to set up price feed watcher: %v", err) - return - } - n.Balances = core.NewAddressBalances(cleanupInterval) defer n.Balances.StopCleanup() @@ -781,36 +908,16 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { if *cfg.Orchestrator { // Set price per pixel base info - pixelsPerUnit, ok := new(big.Rat).SetString(*cfg.PixelsPerUnit) - if !ok || !pixelsPerUnit.IsInt() { - panic(fmt.Errorf("-pixelsPerUnit must be a valid integer, provided %v", *cfg.PixelsPerUnit)) - } - if pixelsPerUnit.Sign() <= 0 { - // Can't divide by 0 - panic(fmt.Errorf("-pixelsPerUnit must be > 0, provided %v", *cfg.PixelsPerUnit)) - } if cfg.PricePerUnit == nil && !*cfg.AIWorker { // Prevent orchestrators from unknowingly providing free transcoding panic(fmt.Errorf("-pricePerUnit must be set")) } else if cfg.PricePerUnit != nil { - pricePerUnit, currency, err := parsePricePerUnit(*cfg.PricePerUnit) - if err != nil { - panic(fmt.Errorf("-pricePerUnit must be a valid integer with an optional currency, provided %v", *cfg.PricePerUnit)) - } else if pricePerUnit.Sign() < 0 { - panic(fmt.Errorf("-pricePerUnit must be >= 0, provided %s", pricePerUnit)) + if *cfg.PricePerUnit < 0 { + panic(fmt.Errorf("-pricePerUnit must be >= 0, provided %d", *cfg.PricePerUnit)) } - pricePerPixel := new(big.Rat).Quo(pricePerUnit, pixelsPerUnit) - autoPrice, err := core.NewAutoConvertedPrice(currency, pricePerPixel, func(price *big.Rat) { - unit := "pixel" - if *cfg.AIWorker { - unit = "compute unit" - } - glog.Infof("Price: %v wei per %s\n", price.FloatString(3), unit) - }) - if err != nil { - panic(fmt.Errorf("Error converting price: %v", err)) - } - n.SetBasePrice("default", autoPrice) + + n.SetBasePrice("default", big.NewRat(int64(*cfg.PricePerUnit), int64(*cfg.PixelsPerUnit))) + glog.Infof("Price: %d wei for %d pixels\n ", *cfg.PricePerUnit, *cfg.PixelsPerUnit) } if *cfg.PricePerBroadcaster != "" { @@ -819,15 +926,9 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { } gatewayPrices := getGatewayPrices(*cfg.PricePerGateway) for _, p := range gatewayPrices { - p := p - pricePerPixel := new(big.Rat).Quo(p.PricePerUnit, p.PixelsPerUnit) - autoPrice, err := core.NewAutoConvertedPrice(p.Currency, pricePerPixel, func(price *big.Rat) { - glog.Infof("Price: %v wei per pixel for gateway %v", price.FloatString(3), p.EthAddress) - }) - if err != nil { - panic(fmt.Errorf("Error converting price for gateway %s: %v", p.EthAddress, err)) - } - n.SetBasePrice(p.EthAddress, autoPrice) + price := big.NewRat(p.PricePerUnit, p.PixelsPerUnit) + n.SetBasePrice(p.EthAddress, price) + glog.Infof("Price: %v set for broadcaster %v", price.RatString(), p.EthAddress) } n.AutoSessionLimit = *cfg.MaxSessions == "auto" @@ -924,30 +1025,12 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { n.Sender = pm.NewSender(n.Eth, timeWatcher, senderWatcher, maxEV, maxTotalEV, *cfg.DepositMultiplier) - pixelsPerUnit, ok := new(big.Rat).SetString(*cfg.PixelsPerUnit) - if !ok || !pixelsPerUnit.IsInt() { - panic(fmt.Errorf("-pixelsPerUnit must be a valid integer, provided %v", *cfg.PixelsPerUnit)) - } - if pixelsPerUnit.Sign() <= 0 { + if *cfg.PixelsPerUnit <= 0 { // Can't divide by 0 - panic(fmt.Errorf("-pixelsPerUnit must be > 0, provided %v", *cfg.PixelsPerUnit)) - } - maxPricePerUnit, currency, err := parsePricePerUnit(*cfg.MaxPricePerUnit) - if err != nil { - panic(fmt.Errorf("The maximum price per unit must be a valid integer with an optional currency, provided %v instead\n", *cfg.MaxPricePerUnit)) + panic(fmt.Errorf("The amount of pixels per unit must be greater than 0, provided %d instead\n", *cfg.PixelsPerUnit)) } - if maxPricePerUnit.Sign() > 0 { - pricePerPixel := new(big.Rat).Quo(maxPricePerUnit, pixelsPerUnit) - autoPrice, err := core.NewAutoConvertedPrice(currency, pricePerPixel, func(price *big.Rat) { - if monitor.Enabled { - monitor.MaxTranscodingPrice(price) - } - glog.Infof("Maximum transcoding price: %v wei per pixel\n ", price.FloatString(3)) - }) - if err != nil { - panic(fmt.Errorf("Error converting price: %v", err)) - } - server.BroadcastCfg.SetMaxPrice(autoPrice) + if *cfg.MaxPricePerUnit > 0 { + server.BroadcastCfg.SetMaxPrice(big.NewRat(int64(*cfg.MaxPricePerUnit), int64(*cfg.PixelsPerUnit))) } else { glog.Infof("Maximum transcoding price per pixel is not greater than 0: %v, broadcaster is currently set to accept ANY price.\n", *cfg.MaxPricePerUnit) glog.Infoln("To update the broadcaster's maximum acceptable transcoding price per pixel, use the CLI or restart the broadcaster with the appropriate 'maxPricePerUnit' and 'pixelsPerUnit' values") @@ -1020,7 +1103,7 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { if *cfg.InitializeRound { // Start round initializer // The node will only initialize rounds if it in the upcoming active set for the round - initializer := eth.NewRoundInitializer(n.Eth, timeWatcher, *cfg.InitializeRoundMaxDelay) + initializer := eth.NewRoundInitializer(n.Eth, timeWatcher) go func() { if err := initializer.Start(); err != nil { serviceErr <- err @@ -1060,201 +1143,6 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { }() } - var aiCaps []core.Capability - capabilityConstraints := make(map[core.Capability]*core.PerCapabilityConstraints) - - if *cfg.AIWorker { - gpus := []string{} - if *cfg.Nvidia != "" { - var err error - gpus, err = common.ParseAccelDevices(*cfg.Nvidia, ffmpeg.Nvidia) - if err != nil { - glog.Errorf("Error parsing -nvidia for devices: %v", err) - return - } - } - - modelsDir := *cfg.AIModelsDir - if modelsDir == "" { - var err error - modelsDir, err = filepath.Abs(path.Join(*cfg.Datadir, "models")) - if err != nil { - glog.Error("Error creating absolute path for models dir: %v", modelsDir) - return - } - } - - if err := os.MkdirAll(modelsDir, 0755); err != nil { - glog.Error("Error creating models dir %v", modelsDir) - return - } - - n.AIWorker, err = worker.NewWorker(*cfg.AIRunnerImage, gpus, modelsDir) - if err != nil { - glog.Errorf("Error starting AI worker: %v", err) - return - } - - // Get base pixels per unit. - pixelsPerUnitBase, ok := new(big.Rat).SetString(*cfg.PixelsPerUnit) - if !ok || !pixelsPerUnitBase.IsInt() { - panic(fmt.Errorf("-pixelsPerUnit must be a valid integer, provided %v", *cfg.PixelsPerUnit)) - } - if !ok || pixelsPerUnitBase.Sign() <= 0 { - // Can't divide by 0 - panic(fmt.Errorf("-pixelsPerUnit must be > 0, provided %v", *cfg.PixelsPerUnit)) - } - - if *cfg.AIModels != "" { - configs, err := core.ParseAIModelConfigs(*cfg.AIModels) - if err != nil { - glog.Errorf("Error parsing -aiModels: %v", err) - return - } - - for _, config := range configs { - modelConstraint := &core.ModelConstraint{Warm: config.Warm} - - var autoPrice *core.AutoConvertedPrice - if *cfg.Network != "offchain" { - pixelsPerUnit := config.PixelsPerUnit.Rat - if config.PixelsPerUnit.Rat == nil { - pixelsPerUnit = pixelsPerUnitBase - } else { - if !pixelsPerUnit.IsInt() || pixelsPerUnit.Sign() <= 0 { - panic(fmt.Errorf("'pixelsPerUnit' value specified for model '%v' in pipeline '%v' must be a valid positive integer, provided %v", config.ModelID, config.Pipeline, config.PixelsPerUnit)) - } - } - pricePerUnit := config.PricePerUnit.Rat - if err != nil { - panic(fmt.Errorf("'pricePerUnit' value specified for model '%v' in pipeline '%v' must be a valid integer with an optional currency, provided %v", config.ModelID, config.Pipeline, config.PricePerUnit)) - } else if pricePerUnit.Sign() < 0 { - panic(fmt.Errorf("'pricePerUnit' value specified for model '%v' in pipeline '%v' must be >= 0, provided %v", config.ModelID, config.Pipeline, config.PricePerUnit)) - } - pricePerPixel := new(big.Rat).Quo(pricePerUnit, pixelsPerUnit) - - autoPrice, err = core.NewAutoConvertedPrice(config.Currency, pricePerPixel, nil) - if err != nil { - panic(fmt.Errorf("error converting price: %v", err)) - } - } - - // If the config contains a URL we call Warm() anyway because AIWorker will just register - // the endpoint for an external container - if config.Warm || config.URL != "" { - endpoint := worker.RunnerEndpoint{URL: config.URL, Token: config.Token} - if err := n.AIWorker.Warm(ctx, config.Pipeline, config.ModelID, endpoint, config.OptimizationFlags); err != nil { - glog.Errorf("Error AI worker warming %v container: %v", config.Pipeline, err) - return - } - } - - // Show warning if people set OptimizationFlags but not Warm. - if len(config.OptimizationFlags) > 0 && !config.Warm { - glog.Warningf("Model %v has 'optimization_flags' set without 'warm'. Optimization flags are currently only used for warm containers.", config.ModelID) - } - - switch config.Pipeline { - case "text-to-image": - _, ok := capabilityConstraints[core.Capability_TextToImage] - if !ok { - aiCaps = append(aiCaps, core.Capability_TextToImage) - capabilityConstraints[core.Capability_TextToImage] = &core.PerCapabilityConstraints{ - Models: make(map[string]*core.ModelConstraint), - } - } - - capabilityConstraints[core.Capability_TextToImage].Models[config.ModelID] = modelConstraint - - if *cfg.Network != "offchain" { - n.SetBasePriceForCap("default", core.Capability_TextToImage, config.ModelID, autoPrice) - } - case "image-to-image": - _, ok := capabilityConstraints[core.Capability_ImageToImage] - if !ok { - aiCaps = append(aiCaps, core.Capability_ImageToImage) - capabilityConstraints[core.Capability_ImageToImage] = &core.PerCapabilityConstraints{ - Models: make(map[string]*core.ModelConstraint), - } - } - - capabilityConstraints[core.Capability_ImageToImage].Models[config.ModelID] = modelConstraint - - if *cfg.Network != "offchain" { - n.SetBasePriceForCap("default", core.Capability_ImageToImage, config.ModelID, autoPrice) - } - case "image-to-video": - _, ok := capabilityConstraints[core.Capability_ImageToVideo] - if !ok { - aiCaps = append(aiCaps, core.Capability_ImageToVideo) - capabilityConstraints[core.Capability_ImageToVideo] = &core.PerCapabilityConstraints{ - Models: make(map[string]*core.ModelConstraint), - } - } - - capabilityConstraints[core.Capability_ImageToVideo].Models[config.ModelID] = modelConstraint - - if *cfg.Network != "offchain" { - n.SetBasePriceForCap("default", core.Capability_ImageToVideo, config.ModelID, autoPrice) - } - case "upscale": - _, ok := capabilityConstraints[core.Capability_Upscale] - if !ok { - aiCaps = append(aiCaps, core.Capability_Upscale) - capabilityConstraints[core.Capability_Upscale] = &core.PerCapabilityConstraints{ - Models: make(map[string]*core.ModelConstraint), - } - } - - capabilityConstraints[core.Capability_Upscale].Models[config.ModelID] = modelConstraint - - if *cfg.Network != "offchain" { - n.SetBasePriceForCap("default", core.Capability_Upscale, config.ModelID, autoPrice) - } - case "audio-to-text": - _, ok := capabilityConstraints[core.Capability_AudioToText] - if !ok { - aiCaps = append(aiCaps, core.Capability_AudioToText) - capabilityConstraints[core.Capability_AudioToText] = &core.PerCapabilityConstraints{ - Models: make(map[string]*core.ModelConstraint), - } - } - - capabilityConstraints[core.Capability_AudioToText].Models[config.ModelID] = modelConstraint - - if *cfg.Network != "offchain" { - n.SetBasePriceForCap("default", core.Capability_AudioToText, config.ModelID, autoPrice) - } - } - - if len(aiCaps) > 0 { - capability := aiCaps[len(aiCaps)-1] - price := n.GetBasePriceForCap("default", capability, config.ModelID) - if *cfg.Network != "offchain" { - pricePerUnit := price.Num().Int64() / price.Denom().Int64() - glog.V(6).Infof("Capability %s (ID: %v) advertised with model constraint %s at price %d wei per compute unit", config.Pipeline, capability, config.ModelID, pricePerUnit) - } else { - glog.V(6).Infof("Capability %s (ID: %v) advertised with model constraint %s", config.Pipeline, capability, config.ModelID) - } - } - } - } else { - glog.Error("The '-aiModels' flag was set, but no model configuration was provided. Please specify the model configuration using the '-aiModels' flag.") - return - } - - defer func() { - ctx, cancel := context.WithTimeout(context.Background(), aiWorkerContainerStopTimeout) - defer cancel() - if err := n.AIWorker.Stop(ctx); err != nil { - glog.Errorf("Error stopping AI worker containers: %v", err) - return - } - - glog.Infof("Stopped AI worker containers") - }() - } - if *cfg.Objectstore != "" { prepared, err := drivers.PrepareOSURL(*cfg.Objectstore) if err != nil { @@ -1405,10 +1293,7 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { *cfg.CliAddr = defaultAddr(*cfg.CliAddr, "127.0.0.1", TranscoderCliPort) } - n.Capabilities = core.NewCapabilitiesWithConstraints(append(transcoderCaps, aiCaps...), core.MandatoryOCapabilities(), core.Constraints{}, capabilityConstraints) - if cfg.OrchMinLivepeerVersion != nil { - n.Capabilities.SetMinVersionConstraint(*cfg.OrchMinLivepeerVersion) - } + n.Capabilities = core.NewCapabilitiesWithConstraints(append(transcoderCaps, aiCaps...), core.MandatoryOCapabilities(), constraints) if drivers.NodeStorage == nil { // base URI will be empty for broadcasters; that's OK @@ -1711,10 +1596,9 @@ func checkOrStoreChainID(dbh *common.DB, chainID *big.Int) error { } type GatewayPrice struct { - EthAddress string - PricePerUnit *big.Rat - Currency string - PixelsPerUnit *big.Rat + EthAddress string `json:"ethaddress"` + PricePerUnit int64 `json:"priceperunit"` + PixelsPerUnit int64 `json:"pixelsperunit"` } func getGatewayPrices(gatewayPrices string) []GatewayPrice { @@ -1723,20 +1607,18 @@ func getGatewayPrices(gatewayPrices string) []GatewayPrice { } // Format of gatewayPrices json - // {"gateways":[{"ethaddress":"address1","priceperunit":0.5,"currency":"USD","pixelsperunit":1}, {"ethaddress":"address2","priceperunit":0.3,"currency":"USD","pixelsperunit":3}]} + // {"gateways":[{"ethaddress":"address1","priceperunit":1000,"pixelsperunit":1}, {"ethaddress":"address2","priceperunit":2000,"pixelsperunit":3}]} var pricesSet struct { Gateways []struct { - EthAddress string `json:"ethaddress"` - PixelsPerUnit core.JSONRat `json:"pixelsperunit"` - PricePerUnit core.JSONRat `json:"priceperunit"` - Currency string `json:"currency"` + EthAddress string `json:"ethaddress"` + PixelsPerUnit json.RawMessage `json:"pixelsperunit"` + PricePerUnit json.RawMessage `json:"priceperunit"` } `json:"gateways"` // TODO: Keep the old name for backwards compatibility, remove in the future Broadcasters []struct { - EthAddress string `json:"ethaddress"` - PixelsPerUnit core.JSONRat `json:"pixelsperunit"` - PricePerUnit core.JSONRat `json:"priceperunit"` - Currency string `json:"currency"` + EthAddress string `json:"ethaddress"` + PixelsPerUnit json.RawMessage `json:"pixelsperunit"` + PricePerUnit json.RawMessage `json:"priceperunit"` } `json:"broadcasters"` } pricesFileContent, _ := common.ReadFromFile(gatewayPrices) @@ -1757,11 +1639,20 @@ func getGatewayPrices(gatewayPrices string) []GatewayPrice { prices := make([]GatewayPrice, len(allGateways)) for i, p := range allGateways { + pixelsPerUnit, err := strconv.ParseInt(string(p.PixelsPerUnit), 10, 64) + if err != nil { + glog.Errorf("Pixels per unit could not be parsed for gateway %v. must be a valid number, provided %s", p.EthAddress, p.PixelsPerUnit) + continue + } + pricePerUnit, err := strconv.ParseInt(string(p.PricePerUnit), 10, 64) + if err != nil { + glog.Errorf("Price per unit could not be parsed for gateway %v. must be a valid number, provided %s", p.EthAddress, p.PricePerUnit) + continue + } prices[i] = GatewayPrice{ EthAddress: p.EthAddress, - Currency: p.Currency, - PricePerUnit: p.PricePerUnit.Rat, - PixelsPerUnit: p.PixelsPerUnit.Rat, + PricePerUnit: pricePerUnit, + PixelsPerUnit: pixelsPerUnit, } } @@ -1817,22 +1708,6 @@ func parseEthKeystorePath(ethKeystorePath string) (keystorePath, error) { return keystore, nil } -func parsePricePerUnit(pricePerUnitStr string) (*big.Rat, string, error) { - pricePerUnitRex := regexp.MustCompile(`^(\d+(\.\d+)?)([A-z][A-z0-9]*)?$`) - match := pricePerUnitRex.FindStringSubmatch(pricePerUnitStr) - if match == nil { - return nil, "", fmt.Errorf("price must be in the format of , provided %v", pricePerUnitStr) - } - price, currency := match[1], match[3] - - pricePerUnit, ok := new(big.Rat).SetString(price) - if !ok { - return nil, "", fmt.Errorf("price must be a valid number, provided %v", match[1]) - } - - return pricePerUnit, currency, nil -} - func refreshOrchPerfScoreLoop(ctx context.Context, region string, orchPerfScoreURL string, score *common.PerfScore) { for { refreshOrchPerfScore(region, orchPerfScoreURL, score) diff --git a/cmd/livepeer/starter/starter_test.go b/cmd/livepeer/starter/starter_test.go index 60df92789..e72e64c0b 100644 --- a/cmd/livepeer/starter/starter_test.go +++ b/cmd/livepeer/starter/starter_test.go @@ -102,8 +102,8 @@ func TestParseGetGatewayPrices(t *testing.T) { assert.NotNil(prices) assert.Equal(2, len(prices)) - price1 := new(big.Rat).Quo(prices[0].PricePerUnit, prices[0].PixelsPerUnit) - price2 := new(big.Rat).Quo(prices[1].PricePerUnit, prices[1].PixelsPerUnit) + price1 := big.NewRat(prices[0].PricePerUnit, prices[0].PixelsPerUnit) + price2 := big.NewRat(prices[1].PricePerUnit, prices[1].PixelsPerUnit) assert.Equal(big.NewRat(1000, 1), price1) assert.Equal(big.NewRat(2000, 3), price2) } @@ -302,91 +302,3 @@ func TestUpdatePerfScore(t *testing.T) { } require.Equal(t, expScores, scores.Scores) } - -func TestParsePricePerUnit(t *testing.T) { - tests := []struct { - name string - pricePerUnitStr string - expectedPrice *big.Rat - expectedCurrency string - expectError bool - }{ - { - name: "Valid input with integer price", - pricePerUnitStr: "100USD", - expectedPrice: big.NewRat(100, 1), - expectedCurrency: "USD", - expectError: false, - }, - { - name: "Valid input with fractional price", - pricePerUnitStr: "0.13USD", - expectedPrice: big.NewRat(13, 100), - expectedCurrency: "USD", - expectError: false, - }, - { - name: "Valid input with decimal price", - pricePerUnitStr: "99.99EUR", - expectedPrice: big.NewRat(9999, 100), - expectedCurrency: "EUR", - expectError: false, - }, - { - name: "Lower case currency", - pricePerUnitStr: "99.99eur", - expectedPrice: big.NewRat(9999, 100), - expectedCurrency: "eur", - expectError: false, - }, - { - name: "Currency with numbers", - pricePerUnitStr: "420DOG3", - expectedPrice: big.NewRat(420, 1), - expectedCurrency: "DOG3", - expectError: false, - }, - { - name: "No specified currency, empty currency", - pricePerUnitStr: "100", - expectedPrice: big.NewRat(100, 1), - expectedCurrency: "", - expectError: false, - }, - { - name: "Explicit wei currency", - pricePerUnitStr: "100wei", - expectedPrice: big.NewRat(100, 1), - expectedCurrency: "wei", - expectError: false, - }, - { - name: "Invalid number", - pricePerUnitStr: "abcUSD", - expectedPrice: nil, - expectedCurrency: "", - expectError: true, - }, - { - name: "Negative price", - pricePerUnitStr: "-100USD", - expectedPrice: nil, - expectedCurrency: "", - expectError: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - price, currency, err := parsePricePerUnit(tt.pricePerUnitStr) - - if tt.expectError { - assert.Error(t, err) - } else { - require.NoError(t, err) - assert.True(t, tt.expectedPrice.Cmp(price) == 0) - assert.Equal(t, tt.expectedCurrency, currency) - } - }) - } -} diff --git a/cmd/livepeer_cli/wizard.go b/cmd/livepeer_cli/wizard.go index 27ba13017..01801e4fd 100644 --- a/cmd/livepeer_cli/wizard.go +++ b/cmd/livepeer_cli/wizard.go @@ -76,7 +76,7 @@ func (w *wizard) readStringAndValidate(validate func(in string) (string, error)) } } -// readStringYesOrNo reads a single line from stdin, trims spaces and +// readStringYesOrNot reads a single line from stdin, trims spaces and // checks that the string is either y or n func (w *wizard) readStringYesOrNo() string { return w.readStringAndValidate(func(in string) (string, error) { diff --git a/cmd/livepeer_cli/wizard_broadcast.go b/cmd/livepeer_cli/wizard_broadcast.go index 8f2c59e91..b7967b0dd 100644 --- a/cmd/livepeer_cli/wizard_broadcast.go +++ b/cmd/livepeer_cli/wizard_broadcast.go @@ -57,14 +57,10 @@ func (w *wizard) setBroadcastConfig() { fmt.Printf("eg. 1 wei / 10 pixels = 0,1 wei per pixel \n") fmt.Printf("\n") fmt.Printf("Enter amount of pixels that make up a single unit (default: 1 pixel) - ") - // Read numbers as strings not to lose precision and support big numbers - pixelsPerUnit := w.readDefaultString("1") + pixelsPerUnit := w.readDefaultInt(1) fmt.Printf("\n") - fmt.Printf("Enter the currency for the price per unit (default: Wei) - ") - currency := w.readDefaultString("Wei") - fmt.Printf("\n") - fmt.Printf("Enter the maximum price to pay for %s pixels in %s (default: 0) - ", pixelsPerUnit, currency) - maxPricePerUnit := w.readDefaultString("0") + fmt.Printf("Enter the maximum price to pay for %d pixels in Wei (required) - ", pixelsPerUnit) + maxPricePerUnit := w.readDefaultInt(0) opts := w.allTranscodingOptions() if opts == nil { @@ -81,18 +77,12 @@ func (w *wizard) setBroadcastConfig() { } val := url.Values{ - "pixelsPerUnit": {fmt.Sprintf("%v", pixelsPerUnit)}, - "currency": {fmt.Sprintf("%v", currency)}, - "maxPricePerUnit": {fmt.Sprintf("%v", maxPricePerUnit)}, + "pixelsPerUnit": {fmt.Sprintf("%v", strconv.Itoa(pixelsPerUnit))}, + "maxPricePerUnit": {fmt.Sprintf("%v", strconv.Itoa(maxPricePerUnit))}, "transcodingOptions": {fmt.Sprintf("%v", transOpts)}, } - result, ok := httpPostWithParams(fmt.Sprintf("http://%v:%v/setBroadcastConfig", w.host, w.httpPort), val) - if !ok { - fmt.Printf("Error applying configuration: %s\n", result) - } else { - fmt.Printf("Configuration applied successfully\n") - } + httpPostWithParams(fmt.Sprintf("http://%v:%v/setBroadcastConfig", w.host, w.httpPort), val) } func (w *wizard) idListToVideoProfileList(idList string, opts map[int]string) (string, error) { diff --git a/cmd/livepeer_cli/wizard_stats.go b/cmd/livepeer_cli/wizard_stats.go index bea3b0790..c7049e012 100644 --- a/cmd/livepeer_cli/wizard_stats.go +++ b/cmd/livepeer_cli/wizard_stats.go @@ -171,10 +171,14 @@ func (w *wizard) broadcastStats() { } price, transcodingOptions := w.getBroadcastConfig() + priceString := "n/a" + if price != nil { + priceString = fmt.Sprintf("%v wei / %v pixels", price.Num().Int64(), price.Denom().Int64()) + } table := tablewriter.NewWriter(os.Stdout) data := [][]string{ - {"Max Price Per Pixel", formatPricePerPixel(price)}, + {"Max Price Per Pixel", priceString}, {"Broadcast Transcoding Options", transcodingOptions}, {"Deposit", eth.FormatUnits(sender.Deposit, "ETH")}, {"Reserve", eth.FormatUnits(sender.Reserve.FundsRemaining, "ETH")}, @@ -215,6 +219,10 @@ func (w *wizard) orchestratorStats() { fmt.Println("+------------------+") table := tablewriter.NewWriter(os.Stdout) + basePrice := "n/a" + if priceInfo != nil { + basePrice = fmt.Sprintf("%v wei / %v pixels", priceInfo.Num(), priceInfo.Denom()) + } data := [][]string{ {"Status", t.Status}, {"Active", strconv.FormatBool(t.Active)}, @@ -223,7 +231,7 @@ func (w *wizard) orchestratorStats() { {"Reward Cut (%)", eth.FormatPerc(t.RewardCut)}, {"Fee Cut (%)", eth.FormatPerc(flipPerc(t.FeeShare))}, {"Last Reward Round", t.LastRewardRound.String()}, - {"Base price per pixel", formatPricePerPixel(priceInfo)}, + {"Base price per pixel", basePrice}, {"Base price for broadcasters", b_prices}, } @@ -484,9 +492,7 @@ func (w *wizard) getBroadcasterPrices() (string, error) { return "", err } - var status struct { - BroadcasterPrices map[string]*big.Rat `json:"BroadcasterPrices"` - } + var status map[string]interface{} err = json.Unmarshal(result, &status) if err != nil { return "", err @@ -494,21 +500,13 @@ func (w *wizard) getBroadcasterPrices() (string, error) { prices := new(bytes.Buffer) - for b, p := range status.BroadcasterPrices { - if b != "default" { - fmt.Fprintf(prices, "%s: %s\n", b, formatPricePerPixel(p)) + if broadcasterPrices, ok := status["BroadcasterPrices"]; ok { + for b, p := range broadcasterPrices.(map[string]interface{}) { + if b != "default" { + fmt.Fprintf(prices, "%s: %s per pixel\n", b, p) + } } } return prices.String(), nil } - -func formatPricePerPixel(price *big.Rat) string { - if price == nil { - return "n/a" - } - if price.IsInt() { - return fmt.Sprintf("%v wei/pixel", price.RatString()) - } - return fmt.Sprintf("%v wei/pixel (%v/%v)", price.FloatString(3), price.Num(), price.Denom()) -} diff --git a/cmd/livepeer_cli/wizard_transcoder.go b/cmd/livepeer_cli/wizard_transcoder.go index 2416aadbd..b25644a74 100644 --- a/cmd/livepeer_cli/wizard_transcoder.go +++ b/cmd/livepeer_cli/wizard_transcoder.go @@ -43,7 +43,13 @@ func myHostPort() string { return "https://" + ip + ":" + defaultRPCPort } -func (w *wizard) promptOrchestratorConfig() (blockRewardCut, feeCut float64, pricePerUnit, currency, pixelsPerUnit, serviceURI string) { +func (w *wizard) promptOrchestratorConfig() (float64, float64, int, int, string) { + var ( + blockRewardCut float64 + feeCut float64 + addr string + ) + orch, _, err := w.getOrchestratorInfo() if err != nil || orch == nil { fmt.Println("unable to get current reward cut and fee cut") @@ -62,23 +68,17 @@ func (w *wizard) promptOrchestratorConfig() (blockRewardCut, feeCut float64, pri fmt.Println("eg. 1 wei / 10 pixels = 0,1 wei per pixel") fmt.Println() fmt.Printf("Enter amount of pixels that make up a single unit (default: 1 pixel) ") - // Read numbers as strings not to lose precision and support big numbers - pixelsPerUnit = w.readDefaultString("1") - fmt.Println() - fmt.Printf("Enter the currency for the price per unit (default: Wei) ") - currency = w.readDefaultString("Wei") - fmt.Println() - fmt.Printf("Enter the price for %s pixels in %s (default: 0) ", pixelsPerUnit, currency) - pricePerUnit = w.readDefaultString("0") + pixelsPerUnit := w.readDefaultInt(1) + fmt.Printf("Enter the price for %d pixels in Wei (required) ", pixelsPerUnit) + pricePerUnit := w.readDefaultInt(0) - var addr string if orch.ServiceURI == "" { addr = myHostPort() } else { addr = orch.ServiceURI } fmt.Printf("Enter the public host:port of node (default: %v)", addr) - serviceURI = w.readStringAndValidate(func(in string) (string, error) { + serviceURI := w.readStringAndValidate(func(in string) (string, error) { if "" == in { in = addr } @@ -92,7 +92,7 @@ func (w *wizard) promptOrchestratorConfig() (blockRewardCut, feeCut float64, pri return in, nil }) - return blockRewardCut, 100 - feeCut, pricePerUnit, currency, pixelsPerUnit, serviceURI + return blockRewardCut, 100 - feeCut, pricePerUnit, pixelsPerUnit, serviceURI } func (w *wizard) activateOrchestrator() { @@ -196,14 +196,13 @@ func (w *wizard) setOrchestratorConfig() { } func (w *wizard) getOrchestratorConfigFormValues() url.Values { - blockRewardCut, feeShare, pricePerUnit, currency, pixelsPerUnit, serviceURI := w.promptOrchestratorConfig() + blockRewardCut, feeShare, pricePerUnit, pixelsPerUnit, serviceURI := w.promptOrchestratorConfig() return url.Values{ "blockRewardCut": {fmt.Sprintf("%v", blockRewardCut)}, "feeShare": {fmt.Sprintf("%v", feeShare)}, - "pricePerUnit": {fmt.Sprintf("%v", pricePerUnit)}, - "currency": {fmt.Sprintf("%v", currency)}, - "pixelsPerUnit": {fmt.Sprintf("%v", pixelsPerUnit)}, + "pricePerUnit": {fmt.Sprintf("%v", strconv.Itoa(pricePerUnit))}, + "pixelsPerUnit": {fmt.Sprintf("%v", strconv.Itoa(pixelsPerUnit))}, "serviceURI": {fmt.Sprintf("%v", serviceURI)}, } } @@ -320,22 +319,18 @@ func (w *wizard) setPriceForBroadcaster() { return in, nil }) - fmt.Println("Enter pixels per unit (default: 1 pixel)") - // Read numbers as strings not to lose precision and support big numbers - pixels := w.readDefaultString("1") - fmt.Println("Enter currency for the price per unit (default: Wei)") - currency := w.readDefaultString("Wei") - fmt.Println("Enter price per unit (default: 0)") - price := w.readDefaultString("0") + fmt.Println("Enter price per unit:") + price := w.readDefaultInt(0) + fmt.Println("Enter pixels per unit:") + pixels := w.readDefaultInt(1) data := url.Values{ - "pricePerUnit": {fmt.Sprintf("%v", price)}, - "currency": {fmt.Sprintf("%v", currency)}, - "pixelsPerUnit": {fmt.Sprintf("%v", pixels)}, + "pricePerUnit": {fmt.Sprintf("%v", strconv.Itoa(price))}, + "pixelsPerUnit": {fmt.Sprintf("%v", strconv.Itoa(pixels))}, "broadcasterEthAddr": {fmt.Sprintf("%v", ethaddr)}, } result, ok := httpPostWithParams(fmt.Sprintf("http://%v:%v/setPriceForBroadcaster", w.host, w.httpPort), data) if ok { - fmt.Printf("Price for broadcaster %v set to %v %v per %v pixels", ethaddr, price, currency, pixels) + fmt.Printf("Price for broadcaster %v set to %v gwei per %v pixels", ethaddr, price, pixels) return } else { fmt.Printf("Error setting price for broadcaster: %v", result) diff --git a/common/types.go b/common/types.go index dcb258add..3e9800304 100644 --- a/common/types.go +++ b/common/types.go @@ -106,7 +106,7 @@ type OrchestratorPool interface { } type SelectionAlgorithm interface { - Select(ctx context.Context, addrs []ethcommon.Address, stakes map[ethcommon.Address]int64, maxPrice *big.Rat, prices map[ethcommon.Address]*big.Rat, perfScores map[ethcommon.Address]float64) ethcommon.Address + Select(addrs []ethcommon.Address, stakes map[ethcommon.Address]int64, prices map[ethcommon.Address]float64, perfScores map[ethcommon.Address]float64) ethcommon.Address } type PerfScore struct { diff --git a/core/ai.go b/core/ai.go index 9ac86a307..772712e97 100644 --- a/core/ai.go +++ b/core/ai.go @@ -4,8 +4,6 @@ import ( "context" "encoding/json" "errors" - "fmt" - "math/big" "os" "regexp" "strconv" @@ -25,34 +23,34 @@ type AI interface { HasCapacity(pipeline, modelID string) bool } -// Custom type to parse a big.Rat from a JSON number. -type JSONRat struct{ *big.Rat } - -func (s *JSONRat) UnmarshalJSON(data []byte) error { - rat, ok := new(big.Rat).SetString(string(data)) - if !ok { - return fmt.Errorf("value is not a number: %s", data) - } - *s = JSONRat{rat} - return nil -} - -func (s JSONRat) String() string { - return s.FloatString(2) -} - type AIModelConfig struct { Pipeline string `json:"pipeline"` ModelID string `json:"model_id"` URL string `json:"url,omitempty"` Token string `json:"token,omitempty"` Warm bool `json:"warm,omitempty"` - PricePerUnit JSONRat `json:"price_per_unit,omitempty"` - PixelsPerUnit JSONRat `json:"pixels_per_unit,omitempty"` - Currency string `json:"currency,omitempty"` + PricePerUnit int64 `json:"price_per_unit,omitempty"` + PixelsPerUnit int64 `json:"pixels_per_unit,omitempty"` OptimizationFlags worker.OptimizationFlags `json:"optimization_flags,omitempty"` } +func (config *AIModelConfig) UnmarshalJSON(data []byte) error { + // Custom type to avoid recursive calls to UnmarshalJSON + type AIModelConfigAlias AIModelConfig + // Set default values for fields + defaultConfig := &AIModelConfigAlias{ + PixelsPerUnit: 1, + } + + if err := json.Unmarshal(data, defaultConfig); err != nil { + return err + } + + *config = AIModelConfig(*defaultConfig) + + return nil +} + func ParseAIModelConfigs(config string) ([]AIModelConfig, error) { var configs []AIModelConfig diff --git a/core/autoconvertedprice.go b/core/autoconvertedprice.go deleted file mode 100644 index b248937db..000000000 --- a/core/autoconvertedprice.go +++ /dev/null @@ -1,139 +0,0 @@ -package core - -import ( - "context" - "fmt" - "math/big" - "strings" - "sync" - - "github.com/livepeer/go-livepeer/eth" - "github.com/livepeer/go-livepeer/eth/watchers" -) - -// PriceFeedWatcher is a global instance of a PriceFeedWatcher. It must be -// initialized before creating an AutoConvertedPrice instance. -var PriceFeedWatcher watchers.PriceFeedWatcher - -// Number of wei in 1 ETH -var weiPerETH = big.NewRat(1e18, 1) - -// AutoConvertedPrice represents a price that is automatically converted to wei -// based on the current price of ETH in a given currency. It uses the static -// PriceFeedWatcher that must be configured before creating an instance. -type AutoConvertedPrice struct { - cancelSubscription func() - onUpdate func(*big.Rat) - basePrice *big.Rat - - mu sync.RWMutex - current *big.Rat -} - -// NewFixedPrice creates a new AutoConvertedPrice with a fixed price in wei. -func NewFixedPrice(price *big.Rat) *AutoConvertedPrice { - return &AutoConvertedPrice{current: price} -} - -// NewAutoConvertedPrice creates a new AutoConvertedPrice instance with the given -// currency and base price. The onUpdate function is optional and gets called -// whenever the price is updated (also with the initial price). The Stop function -// must be called to free resources when the price is no longer needed. -func NewAutoConvertedPrice(currency string, basePrice *big.Rat, onUpdate func(*big.Rat)) (*AutoConvertedPrice, error) { - if onUpdate == nil { - onUpdate = func(*big.Rat) {} - } - - // Default currency (wei/eth) doesn't need the conversion loop - if lcurr := strings.ToLower(currency); lcurr == "" || lcurr == "wei" || lcurr == "eth" { - price := basePrice - if lcurr == "eth" { - price = new(big.Rat).Mul(basePrice, weiPerETH) - } - onUpdate(price) - return NewFixedPrice(price), nil - } - - if PriceFeedWatcher == nil { - return nil, fmt.Errorf("PriceFeedWatcher is not initialized") - } - - base, quote, err := PriceFeedWatcher.Currencies() - if err != nil { - return nil, fmt.Errorf("error getting price feed currencies: %v", err) - } - base, quote, currency = strings.ToUpper(base), strings.ToUpper(quote), strings.ToUpper(currency) - if base != "ETH" && quote != "ETH" { - return nil, fmt.Errorf("price feed does not have ETH as a currency (%v/%v)", base, quote) - } - if base != currency && quote != currency { - return nil, fmt.Errorf("price feed does not have %v as a currency (%v/%v)", currency, base, quote) - } - - currencyPrice, err := PriceFeedWatcher.Current() - if err != nil { - return nil, fmt.Errorf("error getting current price data: %v", err) - } - - ctx, cancel := context.WithCancel(context.Background()) - price := &AutoConvertedPrice{ - cancelSubscription: cancel, - onUpdate: onUpdate, - basePrice: basePrice, - current: new(big.Rat).Mul(basePrice, currencyToWeiMultiplier(currencyPrice, base)), - } - // Trigger the initial update with the current price - onUpdate(price.current) - - price.startAutoConvertLoop(ctx, base) - - return price, nil -} - -// Value returns the current price in wei. -func (a *AutoConvertedPrice) Value() *big.Rat { - a.mu.RLock() - defer a.mu.RUnlock() - return a.current -} - -// Stop unsubscribes from the price feed and frees resources from the -// auto-conversion loop. -func (a *AutoConvertedPrice) Stop() { - a.mu.Lock() - defer a.mu.Unlock() - if a.cancelSubscription != nil { - a.cancelSubscription() - a.cancelSubscription = nil - } -} - -func (a *AutoConvertedPrice) startAutoConvertLoop(ctx context.Context, baseCurrency string) { - priceUpdated := make(chan eth.PriceData, 1) - PriceFeedWatcher.Subscribe(ctx, priceUpdated) - go func() { - for { - select { - case <-ctx.Done(): - return - case currencyPrice := <-priceUpdated: - a.mu.Lock() - a.current = new(big.Rat).Mul(a.basePrice, currencyToWeiMultiplier(currencyPrice, baseCurrency)) - a.mu.Unlock() - - a.onUpdate(a.current) - } - } - }() -} - -// currencyToWeiMultiplier calculates the multiplier to convert the value -// specified in the custom currency to wei. -func currencyToWeiMultiplier(data eth.PriceData, baseCurrency string) *big.Rat { - ethMultipler := data.Price - if baseCurrency == "ETH" { - // Invert the multiplier if the quote is in the form ETH / X - ethMultipler = new(big.Rat).Inv(ethMultipler) - } - return new(big.Rat).Mul(ethMultipler, weiPerETH) -} diff --git a/core/autoconvertedprice_test.go b/core/autoconvertedprice_test.go deleted file mode 100644 index 54db6cfde..000000000 --- a/core/autoconvertedprice_test.go +++ /dev/null @@ -1,258 +0,0 @@ -package core - -import ( - "context" - "math/big" - "testing" - "time" - - "github.com/livepeer/go-livepeer/eth" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" -) - -func TestNewAutoConvertedPrice(t *testing.T) { - t.Run("PriceFeedWatcher not initialized", func(t *testing.T) { - _, err := NewAutoConvertedPrice("USD", big.NewRat(1, 1), nil) - require.Error(t, err) - }) - - watcherMock := NewPriceFeedWatcherMock(t) - PriceFeedWatcher = watcherMock - watcherMock.On("Currencies").Return("ETH", "USD", nil) - - t.Run("Fixed price for wei", func(t *testing.T) { - price, err := NewAutoConvertedPrice("wei", big.NewRat(1, 1), nil) - require.NoError(t, err) - require.Equal(t, big.NewRat(1, 1), price.Value()) - require.Nil(t, price.cancelSubscription) - }) - - t.Run("Auto-converted price for ETH", func(t *testing.T) { - price, err := NewAutoConvertedPrice("ETH", big.NewRat(2, 1), nil) - require.NoError(t, err) - require.Equal(t, big.NewRat(2e18, 1), price.Value()) // 2 ETH in wei - require.Nil(t, price.cancelSubscription) - }) - - t.Run("Auto-converted price for USD", func(t *testing.T) { - watcherMock.On("Current").Return(eth.PriceData{Price: big.NewRat(100, 1)}, nil) - watcherMock.On("Subscribe", mock.Anything, mock.Anything).Once() - price, err := NewAutoConvertedPrice("USD", big.NewRat(2, 1), nil) - require.NoError(t, err) - require.Equal(t, big.NewRat(2e16, 1), price.Value()) // 2 USD * 1/100 ETH/USD - require.NotNil(t, price.cancelSubscription) - price.Stop() - }) - - t.Run("Currency not supported by feed", func(t *testing.T) { - _, err := NewAutoConvertedPrice("GBP", big.NewRat(1, 1), nil) - require.Error(t, err) - }) - - t.Run("Currency ETH not supported by feed", func(t *testing.T) { - // set up a new mock to change the currencies returned - watcherMock := NewPriceFeedWatcherMock(t) - PriceFeedWatcher = watcherMock - watcherMock.On("Currencies").Return("wei", "USD", nil) - - _, err := NewAutoConvertedPrice("USD", big.NewRat(1, 1), nil) - require.Error(t, err) - }) - - t.Run("Auto-converted price for inverted quote", func(t *testing.T) { - // set up a new mock to change the currencies returned - watcherMock := NewPriceFeedWatcherMock(t) - PriceFeedWatcher = watcherMock - watcherMock.On("Currencies").Return("USD", "ETH", nil) - watcherMock.On("Current").Return(eth.PriceData{Price: big.NewRat(1, 420)}, nil) - watcherMock.On("Subscribe", mock.Anything, mock.Anything).Once() - price, err := NewAutoConvertedPrice("USD", big.NewRat(66, 1), nil) - require.NoError(t, err) - require.Equal(t, big.NewRat(11e17, 7), price.Value()) // 66 USD * 1/420 ETH/USD - require.NotNil(t, price.cancelSubscription) - price.Stop() - }) -} - -func TestAutoConvertedPrice_Update(t *testing.T) { - require := require.New(t) - watcherMock := NewPriceFeedWatcherMock(t) - PriceFeedWatcher = watcherMock - - watcherMock.On("Currencies").Return("ETH", "USD", nil) - watcherMock.On("Current").Return(eth.PriceData{Price: big.NewRat(3000, 1)}, nil) - - priceUpdatedChan := make(chan *big.Rat, 1) - onUpdate := func(price *big.Rat) { - priceUpdatedChan <- price - } - - var sink chan<- eth.PriceData - watcherMock.On("Subscribe", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { - sink = args.Get(1).(chan<- eth.PriceData) - }).Once() - - price, err := NewAutoConvertedPrice("USD", big.NewRat(50, 1), onUpdate) - require.NoError(err) - require.NotNil(t, price.cancelSubscription) - defer price.Stop() - watcherMock.AssertExpectations(t) - - require.Equal(big.NewRat(5e16, 3), price.Value()) // 50 USD * 1/3000 ETH/USD - require.Equal(big.NewRat(5e16, 3), <-priceUpdatedChan) // initial update must be sent - - // Simulate a price update - sink <- eth.PriceData{Price: big.NewRat(6000, 1)} - - select { - case updatedPrice := <-priceUpdatedChan: - require.Equal(big.NewRat(5e16, 6), updatedPrice) // 50 USD * 1/6000 USD/ETH - require.Equal(big.NewRat(5e16, 6), price.Value()) // must also udpate current value - case <-time.After(time.Second): - t.Fatal("Expected price update not received") - } -} - -func TestAutoConvertedPrice_Stop(t *testing.T) { - require := require.New(t) - watcherMock := NewPriceFeedWatcherMock(t) - PriceFeedWatcher = watcherMock - - watcherMock.On("Currencies").Return("ETH", "USD", nil) - watcherMock.On("Current").Return(eth.PriceData{Price: big.NewRat(100, 1)}, nil) - - var subsCtx context.Context - watcherMock.On("Subscribe", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { - subsCtx = args.Get(0).(context.Context) - }).Once() - - price, err := NewAutoConvertedPrice("USD", big.NewRat(50, 1), nil) - require.NoError(err) - require.NotNil(t, price.cancelSubscription) - - price.Stop() - require.Nil(price.cancelSubscription) - require.Error(subsCtx.Err()) -} - -func TestCurrencyToWeiMultiplier(t *testing.T) { - tests := []struct { - name string - data eth.PriceData - baseCurrency string - expectedWei *big.Rat - }{ - { - name: "Base currency is ETH", - data: eth.PriceData{Price: big.NewRat(500, 1)}, // 500 USD per ETH - baseCurrency: "ETH", - expectedWei: big.NewRat(1e18, 500), // (1 / 500 USD/ETH) * 1e18 wei/ETH - }, - { - name: "Base currency is not ETH", - data: eth.PriceData{Price: big.NewRat(1, 2000)}, // 1/2000 ETH per USD - baseCurrency: "USD", - expectedWei: big.NewRat(5e14, 1), // (1 * 1/2000 ETH/USD) * 1e18 wei/ETH - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := currencyToWeiMultiplier(tt.data, tt.baseCurrency) - assert.Equal(t, 0, tt.expectedWei.Cmp(result)) - }) - } -} - -// Auto-generated code from here down. -// -// Code generated by mockery v2.42.1. DO NOT EDIT. - -// PriceFeedWatcherMock is an autogenerated mock type for the PriceFeedWatcher type -type PriceFeedWatcherMock struct { - mock.Mock -} - -// Currencies provides a mock function with given fields: -func (_m *PriceFeedWatcherMock) Currencies() (string, string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Currencies") - } - - var r0 string - var r1 string - var r2 error - if rf, ok := ret.Get(0).(func() (string, string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() string); ok { - r1 = rf() - } else { - r1 = ret.Get(1).(string) - } - - if rf, ok := ret.Get(2).(func() error); ok { - r2 = rf() - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// Current provides a mock function with given fields: -func (_m *PriceFeedWatcherMock) Current() (eth.PriceData, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Current") - } - - var r0 eth.PriceData - var r1 error - if rf, ok := ret.Get(0).(func() (eth.PriceData, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() eth.PriceData); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(eth.PriceData) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Subscribe provides a mock function with given fields: ctx, sink -func (_m *PriceFeedWatcherMock) Subscribe(ctx context.Context, sink chan<- eth.PriceData) { - _m.Called(ctx, sink) -} - -// NewPriceFeedWatcherMock creates a new instance of PriceFeedWatcherMock. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewPriceFeedWatcherMock(t interface { - mock.TestingT - Cleanup(func()) -}) *PriceFeedWatcherMock { - mock := &PriceFeedWatcherMock{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/core/capabilities.go b/core/capabilities.go index 237790e3c..12eb7bbc6 100644 --- a/core/capabilities.go +++ b/core/capabilities.go @@ -3,10 +3,9 @@ package core import ( "errors" "fmt" + "sync" - "github.com/Masterminds/semver/v3" - "github.com/golang/glog" "github.com/livepeer/go-livepeer/net" "github.com/livepeer/go-tools/drivers" "github.com/livepeer/lpms/ffmpeg" @@ -21,21 +20,16 @@ type ModelConstraint struct { type Capability int type CapabilityString []uint64 type Constraints struct { - minVersion string -} -type PerCapabilityConstraints struct { // Models contains a *ModelConstraint for each supported model ID Models ModelConstraints } -type CapabilityConstraints map[Capability]*PerCapabilityConstraints +type CapabilityConstraints map[Capability]*Constraints type Capabilities struct { - bitstring CapabilityString - mandatories CapabilityString - version string - constraints Constraints - capabilityConstraints CapabilityConstraints - capacities map[Capability]int - mutex sync.Mutex + bitstring CapabilityString + mandatories CapabilityString + constraints CapabilityConstraints + capacities map[Capability]int + mutex sync.Mutex } type CapabilityTest struct { inVideoData []byte @@ -255,7 +249,7 @@ func (c1 CapabilityConstraints) CompatibleWith(c2 CapabilityConstraints) bool { return true } -func (c1 *PerCapabilityConstraints) CompatibleWith(c2 *PerCapabilityConstraints) bool { +func (c1 *Constraints) CompatibleWith(c2 *Constraints) bool { return c1.Models.CompatibleWith(c2.Models) } @@ -384,51 +378,6 @@ func JobCapabilities(params *StreamParameters, segPar *SegmentParameters) (*Capa return &Capabilities{bitstring: NewCapabilityString(capList)}, nil } -func (bcast *Capabilities) LivepeerVersionCompatibleWith(orch *net.Capabilities) bool { - if bcast == nil || orch == nil || bcast.constraints.minVersion == "" { - // should not happen, but just in case, return true by default - return true - } - if orch.Version == "" || orch.Version == "undefined" { - // Orchestrator/Transcoder version is not set, so it's incompatible - return false - } - - minVer, err := semver.NewVersion(bcast.constraints.minVersion) - if err != nil { - glog.Warningf("error while parsing minVersion: %v", err) - return true - } - ver, err := semver.NewVersion(orch.Version) - if err != nil { - glog.Warningf("error while parsing version: %v", err) - return false - } - - // // Ignore prerelease versions as in go-livepeer we actually define post-release suffixes - // minVerNoSuffix, _ := minVer.SetPrerelease("") - // verNoSuffix, _ := ver.SetPrerelease("") - - // return !verNoSuffix.LessThan(&minVerNoSuffix) - - // TODO: Remove AI-specific cases below when merging into master. - // NOTE: This logic was added to allow the version suffix (i.e. v0.7.6-ai.1) to be - // used correctly during the version constraint filtering. - minVerHasSuffix := minVer.Prerelease() != "" - verHasSuffix := ver.Prerelease() != "" - if !minVerHasSuffix || !verHasSuffix { - minVerNoSuffix, _ := minVer.SetPrerelease("") - verNoSuffix, _ := ver.SetPrerelease("") - minVer = &minVerNoSuffix - ver = &verNoSuffix - } - if minVer.Equal(ver) && minVerHasSuffix && !verHasSuffix { - return false - } - - return !ver.LessThan(minVer) -} - func (bcast *Capabilities) CompatibleWith(orch *net.Capabilities) bool { // Ensure bcast and orch are compatible with one another. @@ -438,9 +387,6 @@ func (bcast *Capabilities) CompatibleWith(orch *net.Capabilities) bool { // cf. common.CapabilityComparator return false } - if !bcast.LivepeerVersionCompatibleWith(orch) { - return false - } // For now, check this: // ( orch.mandatories AND bcast.bitstring ) == orch.mandatories && @@ -453,8 +399,8 @@ func (bcast *Capabilities) CompatibleWith(orch *net.Capabilities) bool { return false } - orchCapabilityConstraints := CapabilitiesFromNetCapabilities(orch).capabilityConstraints - if !bcast.capabilityConstraints.CompatibleWith(orchCapabilityConstraints) { + orchConstraints := CapabilitiesFromNetCapabilities(orch).constraints + if !bcast.constraints.CompatibleWith(orchConstraints) { return false } @@ -467,19 +413,19 @@ func (c *Capabilities) ToNetCapabilities() *net.Capabilities { } c.mutex.Lock() defer c.mutex.Unlock() - netCaps := &net.Capabilities{Bitstring: c.bitstring, Mandatories: c.mandatories, Version: c.version, Capacities: make(map[uint32]uint32), Constraints: &net.Capabilities_Constraints{MinVersion: c.constraints.minVersion}, CapabilityConstraints: make(map[uint32]*net.Capabilities_CapabilityConstraints)} + netCaps := &net.Capabilities{Bitstring: c.bitstring, Mandatories: c.mandatories, Capacities: make(map[uint32]uint32), Constraints: make(map[uint32]*net.Capabilities_Constraints)} for capability, capacity := range c.capacities { netCaps.Capacities[uint32(capability)] = uint32(capacity) } - for capability, constraints := range c.capabilityConstraints { - models := make(map[string]*net.Capabilities_CapabilityConstraints_ModelConstraint) + for capability, constraints := range c.constraints { + models := make(map[string]*net.Capabilities_Constraints_ModelConstraint) for modelID, modelConstraint := range constraints.Models { - models[modelID] = &net.Capabilities_CapabilityConstraints_ModelConstraint{ + models[modelID] = &net.Capabilities_Constraints_ModelConstraint{ Warm: modelConstraint.Warm, } } - netCaps.CapabilityConstraints[uint32(capability)] = &net.Capabilities_CapabilityConstraints{ + netCaps.Constraints[uint32(capability)] = &net.Capabilities_Constraints{ Models: models, } } @@ -491,12 +437,10 @@ func CapabilitiesFromNetCapabilities(caps *net.Capabilities) *Capabilities { return nil } coreCaps := &Capabilities{ - bitstring: caps.Bitstring, - mandatories: caps.Mandatories, - capacities: make(map[Capability]int), - version: caps.Version, - constraints: Constraints{minVersion: caps.Constraints.GetMinVersion()}, - capabilityConstraints: make(CapabilityConstraints), + bitstring: caps.Bitstring, + mandatories: caps.Mandatories, + capacities: make(map[Capability]int), + constraints: make(map[Capability]*Constraints), } if caps.Capacities == nil || len(caps.Capacities) == 0 { // build capacities map if not present (struct received from previous versions) @@ -514,13 +458,13 @@ func CapabilitiesFromNetCapabilities(caps *net.Capabilities) *Capabilities { } } - for capabilityInt, constraints := range caps.CapabilityConstraints { + for capabilityInt, constraints := range caps.Constraints { models := make(map[string]*ModelConstraint) for modelID, modelConstraint := range constraints.Models { models[modelID] = &ModelConstraint{Warm: modelConstraint.Warm} } - coreCaps.capabilityConstraints[Capability(capabilityInt)] = &PerCapabilityConstraints{ + coreCaps.constraints[Capability(capabilityInt)] = &Constraints{ Models: models, } } @@ -529,7 +473,7 @@ func CapabilitiesFromNetCapabilities(caps *net.Capabilities) *Capabilities { } func NewCapabilities(caps []Capability, m []Capability) *Capabilities { - c := &Capabilities{capacities: make(map[Capability]int), version: LivepeerVersion, capabilityConstraints: make(CapabilityConstraints)} + c := &Capabilities{capacities: make(map[Capability]int)} if len(caps) > 0 { c.bitstring = NewCapabilityString(caps) // initialize capacities to 1 by default, mandatory capabilities doesn't have capacities @@ -543,10 +487,9 @@ func NewCapabilities(caps []Capability, m []Capability) *Capabilities { return c } -func NewCapabilitiesWithConstraints(caps []Capability, m []Capability, constraints Constraints, capabilityConstraints CapabilityConstraints) *Capabilities { +func NewCapabilitiesWithConstraints(caps []Capability, m []Capability, constraints CapabilityConstraints) *Capabilities { c := NewCapabilities(caps, m) c.constraints = constraints - c.capabilityConstraints = capabilityConstraints return c } @@ -722,16 +665,3 @@ func (bcast *Capabilities) LegacyOnly() bool { } return bcast.bitstring.CompatibleWith(legacyCapabilityString) } - -func (bcast *Capabilities) SetMinVersionConstraint(minVersionConstraint string) { - if bcast != nil { - bcast.constraints.minVersion = minVersionConstraint - } -} - -func (bcast *Capabilities) MinVersionConstraint() string { - if bcast != nil { - return bcast.constraints.minVersion - } - return "" -} diff --git a/core/capabilities_test.go b/core/capabilities_test.go index 89e3d6210..4f29c6c61 100644 --- a/core/capabilities_test.go +++ b/core/capabilities_test.go @@ -331,65 +331,6 @@ func TestCapability_CompatibleWithNetCap(t *testing.T) { orch = NewCapabilities(nil, nil) bcast = NewCapabilities(nil, []Capability{1}) assert.True(bcast.CompatibleWith(orch.ToNetCapabilities())) - - // broadcaster is not compatible with orchestrator - old O's version - orch = NewCapabilities(nil, nil) - bcast = NewCapabilities(nil, nil) - bcast.constraints.minVersion = "0.4.1" - orch.version = "0.4.0" - assert.False(bcast.CompatibleWith(orch.ToNetCapabilities())) - - // broadcaster is compatible with orchestrator - the same version - orch = NewCapabilities(nil, nil) - bcast = NewCapabilities(nil, nil) - bcast.constraints.minVersion = "0.4.1" - orch.version = "0.4.1" - assert.True(bcast.CompatibleWith(orch.ToNetCapabilities())) - - // TODO: Remove AI-specific cases below when merging into master. - // NOTE: Additional logic was added to the `LivepeerVersionCompatibleWith` method in - // capabilities.go to achieve this behavior. - // AI broadcaster is compatible with AI orchestrator - higher ai suffix - orch = NewCapabilities(nil, nil) - bcast = NewCapabilities(nil, nil) - bcast.constraints.minVersion = "0.7.2" - orch.version = "0.7.2-ai.1" - assert.True(bcast.CompatibleWith(orch.ToNetCapabilities())) - - // AI broadcaster is not compatible with AI orchestrator - no ai suffix - orch = NewCapabilities(nil, nil) - bcast = NewCapabilities(nil, nil) - bcast.constraints.minVersion = "0.7.2-ai.1" - orch.version = "0.7.2" - assert.False(bcast.CompatibleWith(orch.ToNetCapabilities())) - - // AI broadcaster is not compatible with AI orchestrator - lower ai suffix - orch = NewCapabilities(nil, nil) - bcast = NewCapabilities(nil, nil) - bcast.constraints.minVersion = "0.7.2-ai.2" - orch.version = "0.7.2-ai.1" - assert.False(bcast.CompatibleWith(orch.ToNetCapabilities())) - - // AI broadcaster is not compatible with AI orchestrator - lower major version - orch = NewCapabilities(nil, nil) - bcast = NewCapabilities(nil, nil) - bcast.constraints.minVersion = "0.7.2-ai.2" - orch.version = "0.7.1-ai.1" - assert.False(bcast.CompatibleWith(orch.ToNetCapabilities())) - - // AI broadcaster is compatible with AI orchestrator - higher ai suffix - orch = NewCapabilities(nil, nil) - bcast = NewCapabilities(nil, nil) - bcast.constraints.minVersion = "0.7.2-ai.1" - orch.version = "0.7.2-ai.2" - assert.True(bcast.CompatibleWith(orch.ToNetCapabilities())) - - // AI broadcaster is compatible with AI orchestrator- higher major version - orch = NewCapabilities(nil, nil) - bcast = NewCapabilities(nil, nil) - bcast.constraints.minVersion = "0.7.2-ai.2" - orch.version = "0.7.3-ai.1" - assert.True(bcast.CompatibleWith(orch.ToNetCapabilities())) } func TestCapability_RoundTrip_Net(t *testing.T) { @@ -542,131 +483,3 @@ func TestCapabilities_LegacyCheck(t *testing.T) { assert.Len(legacyCapabilities, legacyLen) // sanity check no modifications } - -func TestLiveeerVersionCompatibleWith(t *testing.T) { - tests := []struct { - name string - broadcasterMinVersion string - transcoderVersion string - expected bool - }{ - { - name: "broadcaster required version is the same as the transcoder version", - broadcasterMinVersion: "0.4.1", - transcoderVersion: "0.4.1", - expected: true, - }, - { - name: "broadcaster required version is less than the transcoder version", - broadcasterMinVersion: "0.4.0", - transcoderVersion: "0.4.1", - expected: true, - }, - { - name: "broadcaster required version is more than the transcoder version", - broadcasterMinVersion: "0.4.2", - transcoderVersion: "0.4.1", - expected: false, - }, - { - name: "broadcaster required version is the same as the transcoder dirty version", - broadcasterMinVersion: "0.4.1", - transcoderVersion: "0.4.1-b3278dce-dirty", - expected: true, - }, - { - name: "broadcaster required version is before the transcoder dirty version", - broadcasterMinVersion: "0.4.0", - transcoderVersion: "0.4.1-b3278dce-dirty", - expected: true, - }, - { - name: "broadcaster required version is after the transcoder dirty version", - broadcasterMinVersion: "0.4.2", - transcoderVersion: "0.4.1-b3278dce-dirty", - expected: false, - }, - { - name: "broadcaster required version is empty", - broadcasterMinVersion: "", - transcoderVersion: "0.4.1", - expected: true, - }, - { - name: "both versions are undefined", - broadcasterMinVersion: "", - transcoderVersion: "", - expected: true, - }, - { - name: "transcoder version is empty", - broadcasterMinVersion: "0.4.0", - transcoderVersion: "", - expected: false, - }, - { - name: "transcoder version is undefined", - broadcasterMinVersion: "0.4.0", - transcoderVersion: "undefined", - expected: false, - }, - { - name: "unparsable broadcaster's min version", - broadcasterMinVersion: "nonparsablesemversion", - transcoderVersion: "0.4.1", - expected: true, - }, - { - name: "unparsable transcoder's version", - broadcasterMinVersion: "0.4.1", - transcoderVersion: "nonparsablesemversion", - expected: false, - }, - // TODO: Remove AI-specific cases below when merging into master. - // NOTE: Additional logic was added to the `LivepeerVersionCompatibleWith` method in - // capabilities.go to achieve this behavior. - { - name: "AI broadcaster required version has no AI suffix", - broadcasterMinVersion: "0.7.2", - transcoderVersion: "0.7.2-ai.1", - expected: true, - }, - { - name: "AI transcoder version has no AI suffix", - broadcasterMinVersion: "0.7.2-ai.1", - transcoderVersion: "0.7.2", - expected: false, - }, - { - name: "AI broadcaster required version AI suffix is higher than AI transcoder AI suffix", - broadcasterMinVersion: "0.7.2-ai.2", - transcoderVersion: "0.7.2-ai.1", - expected: false, - }, - { - name: "AI broadcaster required major version is higher than AI transcoder major version", - broadcasterMinVersion: "0.7.2-ai.2", - transcoderVersion: "0.7.2-ai.1", - expected: false, - }, - { - name: "AI broadcaster required version AI suffix is lower than AI transcoder AI suffix", - broadcasterMinVersion: "0.7.2-ai.1", - transcoderVersion: "0.7.2-ai.2", - expected: true, - }, - { - name: "AI broadcaster required major version is lower than AI transcoder major version", - broadcasterMinVersion: "0.7.2-ai.1", - transcoderVersion: "0.7.3-ai.1", - expected: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - bCapabilities := &Capabilities{constraints: Constraints{minVersion: tt.broadcasterMinVersion}} - tCapabilities := &Capabilities{version: tt.transcoderVersion} - assert.Equal(t, tt.expected, bCapabilities.LivepeerVersionCompatibleWith(tCapabilities.ToNetCapabilities())) - }) - } -} diff --git a/core/core_test.go b/core/core_test.go index 5346e2a3d..671c56c91 100644 --- a/core/core_test.go +++ b/core/core_test.go @@ -78,10 +78,10 @@ func TestTranscode(t *testing.T) { } // Check transcode result - if Over1Pct(len(tr.TranscodeData.Segments[0].Data), 273352) { // 144p + if Over1Pct(len(tr.TranscodeData.Segments[0].Data), 218268) { // 144p t.Error("Unexpected transcode result ", len(tr.TranscodeData.Segments[0].Data)) } - if Over1Pct(len(tr.TranscodeData.Segments[1].Data), 378068) { // 240p + if Over1Pct(len(tr.TranscodeData.Segments[1].Data), 302868) { // 240p t.Error("Unexpected transcode result ", len(tr.TranscodeData.Segments[1].Data)) } diff --git a/core/livepeernode.go b/core/livepeernode.go index 0d726ce65..e94c50af2 100644 --- a/core/livepeernode.go +++ b/core/livepeernode.go @@ -64,20 +64,20 @@ func (t NodeType) String() string { } type CapabilityPriceMenu struct { - modelPrices map[string]*AutoConvertedPrice + modelPrices map[string]*big.Rat } func NewCapabilityPriceMenu() CapabilityPriceMenu { return CapabilityPriceMenu{ - modelPrices: make(map[string]*AutoConvertedPrice), + modelPrices: make(map[string]*big.Rat), } } -func (m CapabilityPriceMenu) SetPriceForModelID(modelID string, price *AutoConvertedPrice) { +func (m CapabilityPriceMenu) SetPriceForModelID(modelID string, price *big.Rat) { m.modelPrices[modelID] = price } -func (m CapabilityPriceMenu) PriceForModelID(modelID string) *AutoConvertedPrice { +func (m CapabilityPriceMenu) PriceForModelID(modelID string) *big.Rat { return m.modelPrices[modelID] } @@ -87,7 +87,7 @@ func NewCapabilityPrices() CapabilityPrices { return make(map[Capability]CapabilityPriceMenu) } -func (cp CapabilityPrices) SetPriceForModelID(cap Capability, modelID string, price *AutoConvertedPrice) { +func (cp CapabilityPrices) SetPriceForModelID(cap Capability, modelID string, price *big.Rat) { menu, ok := cp[cap] if !ok { menu = NewCapabilityPriceMenu() @@ -97,7 +97,7 @@ func (cp CapabilityPrices) SetPriceForModelID(cap Capability, modelID string, pr menu.SetPriceForModelID(modelID, price) } -func (cp CapabilityPrices) PriceForModelID(cap Capability, modelID string) *AutoConvertedPrice { +func (cp CapabilityPrices) PriceForModelID(cap Capability, modelID string) *big.Rat { menu, ok := cp[cap] if !ok { return nil @@ -139,7 +139,7 @@ type LivepeerNode struct { StorageConfigs map[string]*transcodeConfig storageMutex *sync.RWMutex // Transcoder private fields - priceInfo map[string]*AutoConvertedPrice + priceInfo map[string]*big.Rat priceInfoForCaps map[string]CapabilityPrices serviceURI url.URL segmentMutex *sync.RWMutex @@ -155,8 +155,8 @@ func NewLivepeerNode(e eth.LivepeerEthClient, wd string, dbh *common.DB) (*Livep AutoAdjustPrice: true, SegmentChans: make(map[ManifestID]SegmentChan), segmentMutex: &sync.RWMutex{}, - Capabilities: &Capabilities{capacities: map[Capability]int{}, version: LivepeerVersion}, - priceInfo: make(map[string]*AutoConvertedPrice), + Capabilities: &Capabilities{capacities: map[Capability]int{}}, + priceInfo: make(map[string]*big.Rat), priceInfoForCaps: make(map[string]CapabilityPrices), StorageConfigs: make(map[string]*transcodeConfig), storageMutex: &sync.RWMutex{}, @@ -176,16 +176,12 @@ func (n *LivepeerNode) SetServiceURI(newUrl *url.URL) { } // SetBasePrice sets the base price for an orchestrator on the node -func (n *LivepeerNode) SetBasePrice(b_eth_addr string, price *AutoConvertedPrice) { +func (n *LivepeerNode) SetBasePrice(b_eth_addr string, price *big.Rat) { addr := strings.ToLower(b_eth_addr) n.mu.Lock() defer n.mu.Unlock() - prevPrice := n.priceInfo[addr] n.priceInfo[addr] = price - if prevPrice != nil { - prevPrice.Stop() - } } // GetBasePrice gets the base price for an orchestrator @@ -194,25 +190,17 @@ func (n *LivepeerNode) GetBasePrice(b_eth_addr string) *big.Rat { n.mu.RLock() defer n.mu.RUnlock() - price := n.priceInfo[addr] - if price == nil { - return nil - } - return price.Value() + return n.priceInfo[addr] } func (n *LivepeerNode) GetBasePrices() map[string]*big.Rat { n.mu.RLock() defer n.mu.RUnlock() - prices := make(map[string]*big.Rat) - for addr, price := range n.priceInfo { - prices[addr] = price.Value() - } - return prices + return n.priceInfo } -func (n *LivepeerNode) SetBasePriceForCap(b_eth_addr string, cap Capability, modelID string, price *AutoConvertedPrice) { +func (n *LivepeerNode) SetBasePriceForCap(b_eth_addr string, cap Capability, modelID string, price *big.Rat) { addr := strings.ToLower(b_eth_addr) n.mu.Lock() defer n.mu.Unlock() @@ -236,7 +224,7 @@ func (n *LivepeerNode) GetBasePriceForCap(b_eth_addr string, cap Capability, mod return nil } - return prices.PriceForModelID(cap, modelID).Value() + return prices.PriceForModelID(cap, modelID) } // SetMaxFaceValue sets the faceValue upper limit for tickets received diff --git a/core/livepeernode_test.go b/core/livepeernode_test.go index d943086ba..259992f89 100644 --- a/core/livepeernode_test.go +++ b/core/livepeernode_test.go @@ -162,8 +162,8 @@ func TestSetAndGetBasePrice(t *testing.T) { price := big.NewRat(1, 1) - n.SetBasePrice("default", NewFixedPrice(price)) - assert.Zero(n.priceInfo["default"].Value().Cmp(price)) + n.SetBasePrice("default", price) + assert.Zero(n.priceInfo["default"].Cmp(price)) assert.Zero(n.GetBasePrice("default").Cmp(price)) assert.Zero(n.GetBasePrices()["default"].Cmp(price)) @@ -172,36 +172,10 @@ func TestSetAndGetBasePrice(t *testing.T) { price1 := big.NewRat(2, 1) price2 := big.NewRat(3, 1) - n.SetBasePrice(addr1, NewFixedPrice(price1)) - n.SetBasePrice(addr2, NewFixedPrice(price2)) - assert.Zero(n.priceInfo[addr1].Value().Cmp(price1)) - assert.Zero(n.priceInfo[addr2].Value().Cmp(price2)) + n.SetBasePrice(addr1, price1) + n.SetBasePrice(addr2, price2) + assert.Zero(n.priceInfo[addr1].Cmp(price1)) + assert.Zero(n.priceInfo[addr2].Cmp(price2)) assert.Zero(n.GetBasePrices()[addr1].Cmp(price1)) assert.Zero(n.GetBasePrices()[addr2].Cmp(price2)) } - -func TestSetAndGetCapabilityPrices(t *testing.T) { - require := require.New(t) - assert := assert.New(t) - - n, err := NewLivepeerNode(nil, "", nil) - require.Nil(err) - - price := big.NewRat(1, 1) - - n.SetBasePriceForCap("default", Capability_TextToImage, "default", NewFixedPrice(price)) - assert.Zero(n.priceInfoForCaps["default"].PriceForModelID(Capability_TextToImage, "default").Value().Cmp(price)) - assert.Zero(n.GetBasePriceForCap("default", Capability_TextToImage, "default").Cmp(price)) - - addr1 := "0x0000000000000000000000000000000000000000" - addr2 := "0x1000000000000000000000000000000000000000" - price1 := big.NewRat(2, 1) - price2 := big.NewRat(3, 1) - - n.SetBasePriceForCap(addr1, Capability_TextToImage, "default", NewFixedPrice(price1)) - n.SetBasePriceForCap(addr2, Capability_ImageToImage, "default", NewFixedPrice(price2)) - assert.Zero(n.priceInfoForCaps[addr1].PriceForModelID(Capability_TextToImage, "default").Value().Cmp(price1)) - assert.Zero(n.priceInfoForCaps[addr2].PriceForModelID(Capability_ImageToImage, "default").Value().Cmp(price2)) - assert.Zero(n.GetBasePriceForCap(addr1, Capability_TextToImage, "default").Cmp(price1)) - assert.Zero(n.GetBasePriceForCap(addr2, Capability_ImageToImage, "default").Cmp(price2)) -} diff --git a/core/orch_test.go b/core/orch_test.go index 72aa9cb8b..981661433 100644 --- a/core/orch_test.go +++ b/core/orch_test.go @@ -245,10 +245,7 @@ func TestSelectTranscoder(t *testing.T) { strm := &StubTranscoderServer{manager: m, WithholdResults: false} strm2 := &StubTranscoderServer{manager: m} - LivepeerVersion = "0.4.1" capabilities := NewCapabilities(DefaultCapabilities(), []Capability{}) - LivepeerVersion = "undefined" - richCapabilities := NewCapabilities(append(DefaultCapabilities(), Capability_HEVC_Encode), []Capability{}) allCapabilities := NewCapabilities(append(DefaultCapabilities(), OptionalCapabilities()...), []Capability{}) @@ -262,7 +259,7 @@ func TestSelectTranscoder(t *testing.T) { go func() { m.Manage(strm, 1, capabilities.ToNetCapabilities()) }() time.Sleep(1 * time.Millisecond) // allow time for first stream to register go func() { m.Manage(strm2, 1, richCapabilities.ToNetCapabilities()); wg.Done() }() - time.Sleep(1 * time.Millisecond) // allow time for second stream to register e for third stream to register + time.Sleep(1 * time.Millisecond) // allow time for second stream to register assert.NotNil(m.liveTranscoders[strm]) assert.NotNil(m.liveTranscoders[strm2]) @@ -344,20 +341,6 @@ func TestSelectTranscoder(t *testing.T) { assert.Equal(1, t1.load) m.completeStreamSession(testSessionId) assert.Equal(0, t1.load) - - // assert one transcoder with the correct Livepeer version is selected - minVersionCapabilities := NewCapabilities(DefaultCapabilities(), []Capability{}) - minVersionCapabilities.SetMinVersionConstraint("0.4.0") - currentTranscoder, err = m.selectTranscoder(testSessionId, minVersionCapabilities) - assert.Nil(err) - m.completeStreamSession(testSessionId) - - // assert no transcoders available for min version higher than any transcoder - minVersionHighCapabilities := NewCapabilities(DefaultCapabilities(), []Capability{}) - minVersionHighCapabilities.SetMinVersionConstraint("0.4.2") - currentTranscoder, err = m.selectTranscoder(testSessionId, minVersionHighCapabilities) - assert.NotNil(err) - m.completeStreamSession(testSessionId) } func TestCompleteStreamSession(t *testing.T) { @@ -721,7 +704,7 @@ func TestProcessPayment_GivenRecipientError_ReturnsNil(t *testing.T) { } orch := NewOrchestrator(n, rm) orch.address = addr - orch.node.SetBasePrice("default", NewFixedPrice(big.NewRat(0, 1))) + orch.node.SetBasePrice("default", big.NewRat(0, 1)) recipient.On("TxCostMultiplier", mock.Anything).Return(big.NewRat(1, 1), nil) recipient.On("ReceiveTicket", mock.Anything, mock.Anything, mock.Anything).Return("", false, nil) @@ -802,7 +785,7 @@ func TestProcessPayment_ActiveOrchestrator(t *testing.T) { } orch := NewOrchestrator(n, rm) orch.address = addr - orch.node.SetBasePrice("default", NewFixedPrice(big.NewRat(0, 1))) + orch.node.SetBasePrice("default", big.NewRat(0, 1)) // orchestrator inactive -> error err := orch.ProcessPayment(context.Background(), defaultPayment(t), ManifestID("some manifest")) @@ -873,7 +856,7 @@ func TestProcessPayment_GivenLosingTicket_DoesNotRedeem(t *testing.T) { } orch := NewOrchestrator(n, rm) orch.address = addr - orch.node.SetBasePrice("default", NewFixedPrice(big.NewRat(0, 1))) + orch.node.SetBasePrice("default", big.NewRat(0, 1)) recipient.On("TxCostMultiplier", mock.Anything).Return(big.NewRat(1, 1), nil) recipient.On("ReceiveTicket", mock.Anything, mock.Anything, mock.Anything).Return("some sessionID", false, nil) @@ -905,7 +888,7 @@ func TestProcessPayment_GivenWinningTicket_RedeemError(t *testing.T) { } orch := NewOrchestrator(n, rm) orch.address = addr - orch.node.SetBasePrice("default", NewFixedPrice(big.NewRat(0, 1))) + orch.node.SetBasePrice("default", big.NewRat(0, 1)) manifestID := ManifestID("some manifest") sessionID := "some sessionID" @@ -945,7 +928,7 @@ func TestProcessPayment_GivenWinningTicket_Redeems(t *testing.T) { } orch := NewOrchestrator(n, rm) orch.address = addr - orch.node.SetBasePrice("default", NewFixedPrice(big.NewRat(0, 1))) + orch.node.SetBasePrice("default", big.NewRat(0, 1)) manifestID := ManifestID("some manifest") sessionID := "some sessionID" @@ -985,7 +968,7 @@ func TestProcessPayment_GivenMultipleWinningTickets_RedeemsAll(t *testing.T) { } orch := NewOrchestrator(n, rm) orch.address = addr - orch.node.SetBasePrice("default", NewFixedPrice(big.NewRat(0, 1))) + orch.node.SetBasePrice("default", big.NewRat(0, 1)) manifestID := ManifestID("some manifest") sessionID := "some sessionID" @@ -1055,7 +1038,7 @@ func TestProcessPayment_GivenConcurrentWinningTickets_RedeemsAll(t *testing.T) { } orch := NewOrchestrator(n, rm) orch.address = addr - orch.node.SetBasePrice("default", NewFixedPrice(big.NewRat(0, 1))) + orch.node.SetBasePrice("default", big.NewRat(0, 1)) manifestIDs := make([]string, 5) @@ -1114,7 +1097,7 @@ func TestProcessPayment_GivenReceiveTicketError_ReturnsError(t *testing.T) { } orch := NewOrchestrator(n, rm) orch.address = addr - orch.node.SetBasePrice("default", NewFixedPrice(big.NewRat(0, 1))) + orch.node.SetBasePrice("default", big.NewRat(0, 1)) manifestID := ManifestID("some manifest") @@ -1182,7 +1165,7 @@ func TestProcessPayment_PaymentError_DoesNotIncreaseCreditBalance(t *testing.T) } orch := NewOrchestrator(n, rm) orch.address = addr - orch.node.SetBasePrice("default", NewFixedPrice(big.NewRat(0, 1))) + orch.node.SetBasePrice("default", big.NewRat(0, 1)) manifestID := ManifestID("some manifest") paymentError := errors.New("ReceiveTicket error") @@ -1244,7 +1227,7 @@ func TestSufficientBalance_IsSufficient_ReturnsTrue(t *testing.T) { } orch := NewOrchestrator(n, rm) orch.address = addr - orch.node.SetBasePrice("default", NewFixedPrice(big.NewRat(0, 1))) + orch.node.SetBasePrice("default", big.NewRat(0, 1)) manifestID := ManifestID("some manifest") @@ -1282,7 +1265,7 @@ func TestSufficientBalance_IsNotSufficient_ReturnsFalse(t *testing.T) { } orch := NewOrchestrator(n, rm) orch.address = addr - orch.node.SetBasePrice("default", NewFixedPrice(big.NewRat(0, 1))) + orch.node.SetBasePrice("default", big.NewRat(0, 1)) manifestID := ManifestID("some manifest") @@ -1324,7 +1307,7 @@ func TestSufficientBalance_OffChainMode_ReturnsTrue(t *testing.T) { func TestTicketParams(t *testing.T) { n, _ := NewLivepeerNode(nil, "", nil) - n.priceInfo["default"] = NewFixedPrice(big.NewRat(1, 1)) + n.priceInfo["default"] = big.NewRat(1, 1) priceInfo := &net.PriceInfo{PricePerUnit: 1, PixelsPerUnit: 1} recipient := new(pm.MockRecipient) n.Recipient = recipient @@ -1405,7 +1388,7 @@ func TestPriceInfo(t *testing.T) { expPricePerPixel := big.NewRat(101, 100) n, _ := NewLivepeerNode(nil, "", nil) - n.SetBasePrice("default", NewFixedPrice(basePrice)) + n.SetBasePrice("default", basePrice) recipient := new(pm.MockRecipient) n.Recipient = recipient @@ -1423,7 +1406,7 @@ func TestPriceInfo(t *testing.T) { // basePrice = 10/1, txMultiplier = 100/1 => expPricePerPixel = 1010/100 basePrice = big.NewRat(10, 1) - n.SetBasePrice("default", NewFixedPrice(basePrice)) + n.SetBasePrice("default", basePrice) orch = NewOrchestrator(n, nil) expPricePerPixel = big.NewRat(1010, 100) @@ -1438,7 +1421,7 @@ func TestPriceInfo(t *testing.T) { // basePrice = 1/10, txMultiplier = 100 => expPricePerPixel = 101/1000 basePrice = big.NewRat(1, 10) - n.SetBasePrice("default", NewFixedPrice(basePrice)) + n.SetBasePrice("default", basePrice) orch = NewOrchestrator(n, nil) expPricePerPixel = big.NewRat(101, 1000) @@ -1452,7 +1435,7 @@ func TestPriceInfo(t *testing.T) { assert.Equal(priceInfo.PixelsPerUnit, expPrice.Denom().Int64()) // basePrice = 25/10 , txMultiplier = 100 => expPricePerPixel = 2525/1000 basePrice = big.NewRat(25, 10) - n.SetBasePrice("default", NewFixedPrice(basePrice)) + n.SetBasePrice("default", basePrice) orch = NewOrchestrator(n, nil) expPricePerPixel = big.NewRat(2525, 1000) @@ -1468,7 +1451,7 @@ func TestPriceInfo(t *testing.T) { // basePrice = 10/1 , txMultiplier = 100/10 => expPricePerPixel = 11 basePrice = big.NewRat(10, 1) txMultiplier = big.NewRat(100, 10) - n.SetBasePrice("default", NewFixedPrice(basePrice)) + n.SetBasePrice("default", basePrice) recipient = new(pm.MockRecipient) n.Recipient = recipient recipient.On("TxCostMultiplier", mock.Anything).Return(txMultiplier, nil) @@ -1487,7 +1470,7 @@ func TestPriceInfo(t *testing.T) { // basePrice = 10/1 , txMultiplier = 1/10 => expPricePerPixel = 110 basePrice = big.NewRat(10, 1) txMultiplier = big.NewRat(1, 10) - n.SetBasePrice("default", NewFixedPrice(basePrice)) + n.SetBasePrice("default", basePrice) recipient = new(pm.MockRecipient) n.Recipient = recipient recipient.On("TxCostMultiplier", mock.Anything).Return(txMultiplier, nil) @@ -1506,7 +1489,7 @@ func TestPriceInfo(t *testing.T) { // basePrice = 10, txMultiplier = 1 => expPricePerPixel = 20 basePrice = big.NewRat(10, 1) txMultiplier = big.NewRat(1, 1) - n.SetBasePrice("default", NewFixedPrice(basePrice)) + n.SetBasePrice("default", basePrice) recipient = new(pm.MockRecipient) n.Recipient = recipient recipient.On("TxCostMultiplier", mock.Anything).Return(txMultiplier, nil) @@ -1523,7 +1506,7 @@ func TestPriceInfo(t *testing.T) { assert.Equal(priceInfo.PixelsPerUnit, expPrice.Denom().Int64()) // basePrice = 0 => expPricePerPixel = 0 - n.SetBasePrice("default", NewFixedPrice(big.NewRat(0, 1))) + n.SetBasePrice("default", big.NewRat(0, 1)) orch = NewOrchestrator(n, nil) priceInfo, err = orch.PriceInfo(ethcommon.Address{}, "") @@ -1533,7 +1516,7 @@ func TestPriceInfo(t *testing.T) { // test no overflows basePrice = big.NewRat(25000, 1) - n.SetBasePrice("default", NewFixedPrice(basePrice)) + n.SetBasePrice("default", basePrice) faceValue, _ := new(big.Int).SetString("22245599237119512", 10) txCost := new(big.Int).Mul(big.NewInt(100000), big.NewInt(7500000000)) txMultiplier = new(big.Rat).SetFrac(faceValue, txCost) // 926899968213313/31250000000000 @@ -1589,7 +1572,7 @@ func TestPriceInfo_TxMultiplierError_ReturnsError(t *testing.T) { expError := errors.New("TxMultiplier Error") n, _ := NewLivepeerNode(nil, "", nil) - n.SetBasePrice("default", NewFixedPrice(big.NewRat(1, 1))) + n.SetBasePrice("default", big.NewRat(1, 1)) recipient := new(pm.MockRecipient) n.Recipient = recipient recipient.On("TxCostMultiplier", mock.Anything).Return(nil, expError) diff --git a/core/orchestrator.go b/core/orchestrator.go index 70975ff3a..4ceea94bb 100644 --- a/core/orchestrator.go +++ b/core/orchestrator.go @@ -350,7 +350,7 @@ func (orch *orchestrator) priceInfo(sender ethcommon.Address, manifestID Manifes for cap := range caps.Capacities { // If the capability does not have constraints (and thus any model constraints) skip it // because we only price a capability together with a model ID right now - constraints, ok := caps.CapabilityConstraints[cap] + constraints, ok := caps.Constraints[cap] if !ok { continue } @@ -1263,9 +1263,7 @@ func (rtm *RemoteTranscoderManager) selectTranscoder(sessionId string, caps *Cap findCompatibleTranscoder := func(rtm *RemoteTranscoderManager) int { for i := len(rtm.remoteTranscoders) - 1; i >= 0; i-- { // no capabilities = default capabilities, all transcoders must support them - if caps == nil || - (caps.bitstring.CompatibleWith(rtm.remoteTranscoders[i].capabilities.bitstring) && - caps.LivepeerVersionCompatibleWith(rtm.remoteTranscoders[i].capabilities.ToNetCapabilities())) { + if caps == nil || caps.bitstring.CompatibleWith(rtm.remoteTranscoders[i].capabilities.bitstring) { return i } } @@ -1317,7 +1315,7 @@ func (node *RemoteTranscoderManager) EndTranscodingSession(sessionId string) { panic("shouldn't be called on RemoteTranscoderManager") } -// completeStreamSession end a stream session for a remote transcoder and decrements its load +// completeStreamSessions end a stream session for a remote transcoder and decrements its load // caller should hold the mutex lock func (rtm *RemoteTranscoderManager) completeStreamSession(sessionId string) { t, ok := rtm.streamSessions[sessionId] diff --git a/core/testdata/rapid/TestCapability_RoundTrip_Net/TestCapability_RoundTrip_Net-20240729130524-3824236.fail b/core/testdata/rapid/TestCapability_RoundTrip_Net/TestCapability_RoundTrip_Net-20240729130524-3824236.fail deleted file mode 100644 index bfa48715a..000000000 --- a/core/testdata/rapid/TestCapability_RoundTrip_Net/TestCapability_RoundTrip_Net-20240729130524-3824236.fail +++ /dev/null @@ -1,828 +0,0 @@ -# 2024/07/29 13:05:24.695037 [TestCapability_RoundTrip_Net] [rapid] draw capLen: 54 -# 2024/07/29 13:05:24.695039 [TestCapability_RoundTrip_Net] [rapid] draw cap: 464 -# 2024/07/29 13:05:24.695040 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 -# 2024/07/29 13:05:24.695040 [TestCapability_RoundTrip_Net] [rapid] draw cap: 461 -# 2024/07/29 13:05:24.695041 [TestCapability_RoundTrip_Net] [rapid] draw cap: 509 -# 2024/07/29 13:05:24.695041 [TestCapability_RoundTrip_Net] [rapid] draw cap: 429 -# 2024/07/29 13:05:24.695042 [TestCapability_RoundTrip_Net] [rapid] draw cap: 39 -# 2024/07/29 13:05:24.695042 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 -# 2024/07/29 13:05:24.695043 [TestCapability_RoundTrip_Net] [rapid] draw cap: 9 -# 2024/07/29 13:05:24.695043 [TestCapability_RoundTrip_Net] [rapid] draw cap: 291 -# 2024/07/29 13:05:24.695044 [TestCapability_RoundTrip_Net] [rapid] draw cap: 4 -# 2024/07/29 13:05:24.695045 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 -# 2024/07/29 13:05:24.695045 [TestCapability_RoundTrip_Net] [rapid] draw cap: 27 -# 2024/07/29 13:05:24.695045 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 -# 2024/07/29 13:05:24.695046 [TestCapability_RoundTrip_Net] [rapid] draw cap: 214 -# 2024/07/29 13:05:24.695046 [TestCapability_RoundTrip_Net] [rapid] draw cap: 192 -# 2024/07/29 13:05:24.695047 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 -# 2024/07/29 13:05:24.695047 [TestCapability_RoundTrip_Net] [rapid] draw cap: 484 -# 2024/07/29 13:05:24.695047 [TestCapability_RoundTrip_Net] [rapid] draw cap: 165 -# 2024/07/29 13:05:24.695048 [TestCapability_RoundTrip_Net] [rapid] draw cap: 177 -# 2024/07/29 13:05:24.695049 [TestCapability_RoundTrip_Net] [rapid] draw cap: 213 -# 2024/07/29 13:05:24.695049 [TestCapability_RoundTrip_Net] [rapid] draw cap: 18 -# 2024/07/29 13:05:24.695050 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 -# 2024/07/29 13:05:24.695050 [TestCapability_RoundTrip_Net] [rapid] draw cap: 9 -# 2024/07/29 13:05:24.695050 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 -# 2024/07/29 13:05:24.695051 [TestCapability_RoundTrip_Net] [rapid] draw cap: 12 -# 2024/07/29 13:05:24.695051 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 -# 2024/07/29 13:05:24.695051 [TestCapability_RoundTrip_Net] [rapid] draw cap: 437 -# 2024/07/29 13:05:24.695052 [TestCapability_RoundTrip_Net] [rapid] draw cap: 11 -# 2024/07/29 13:05:24.695052 [TestCapability_RoundTrip_Net] [rapid] draw cap: 39 -# 2024/07/29 13:05:24.695052 [TestCapability_RoundTrip_Net] [rapid] draw cap: 30 -# 2024/07/29 13:05:24.695053 [TestCapability_RoundTrip_Net] [rapid] draw cap: 12 -# 2024/07/29 13:05:24.695053 [TestCapability_RoundTrip_Net] [rapid] draw cap: 325 -# 2024/07/29 13:05:24.695053 [TestCapability_RoundTrip_Net] [rapid] draw cap: 204 -# 2024/07/29 13:05:24.695054 [TestCapability_RoundTrip_Net] [rapid] draw cap: 13 -# 2024/07/29 13:05:24.695054 [TestCapability_RoundTrip_Net] [rapid] draw cap: 403 -# 2024/07/29 13:05:24.695055 [TestCapability_RoundTrip_Net] [rapid] draw cap: 498 -# 2024/07/29 13:05:24.695055 [TestCapability_RoundTrip_Net] [rapid] draw cap: 512 -# 2024/07/29 13:05:24.695056 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 -# 2024/07/29 13:05:24.695057 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 -# 2024/07/29 13:05:24.695058 [TestCapability_RoundTrip_Net] [rapid] draw cap: 136 -# 2024/07/29 13:05:24.695058 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 -# 2024/07/29 13:05:24.695058 [TestCapability_RoundTrip_Net] [rapid] draw cap: 169 -# 2024/07/29 13:05:24.695059 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 -# 2024/07/29 13:05:24.695059 [TestCapability_RoundTrip_Net] [rapid] draw cap: 62 -# 2024/07/29 13:05:24.695059 [TestCapability_RoundTrip_Net] [rapid] draw cap: 181 -# 2024/07/29 13:05:24.695060 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 -# 2024/07/29 13:05:24.695060 [TestCapability_RoundTrip_Net] [rapid] draw cap: 6 -# 2024/07/29 13:05:24.695061 [TestCapability_RoundTrip_Net] [rapid] draw cap: 12 -# 2024/07/29 13:05:24.695061 [TestCapability_RoundTrip_Net] [rapid] draw cap: 512 -# 2024/07/29 13:05:24.695062 [TestCapability_RoundTrip_Net] [rapid] draw cap: 177 -# 2024/07/29 13:05:24.695062 [TestCapability_RoundTrip_Net] [rapid] draw cap: 7 -# 2024/07/29 13:05:24.695063 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 -# 2024/07/29 13:05:24.695064 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 -# 2024/07/29 13:05:24.695064 [TestCapability_RoundTrip_Net] [rapid] draw cap: 400 -# 2024/07/29 13:05:24.695065 [TestCapability_RoundTrip_Net] [rapid] draw capLen: 42 -# 2024/07/29 13:05:24.695066 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 -# 2024/07/29 13:05:24.695067 [TestCapability_RoundTrip_Net] [rapid] draw cap: 6 -# 2024/07/29 13:05:24.695068 [TestCapability_RoundTrip_Net] [rapid] draw cap: 368 -# 2024/07/29 13:05:24.695069 [TestCapability_RoundTrip_Net] [rapid] draw cap: 512 -# 2024/07/29 13:05:24.695069 [TestCapability_RoundTrip_Net] [rapid] draw cap: 10 -# 2024/07/29 13:05:24.695070 [TestCapability_RoundTrip_Net] [rapid] draw cap: 235 -# 2024/07/29 13:05:24.695071 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 -# 2024/07/29 13:05:24.695071 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 -# 2024/07/29 13:05:24.695071 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 -# 2024/07/29 13:05:24.695072 [TestCapability_RoundTrip_Net] [rapid] draw cap: 8 -# 2024/07/29 13:05:24.695072 [TestCapability_RoundTrip_Net] [rapid] draw cap: 263 -# 2024/07/29 13:05:24.695072 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 -# 2024/07/29 13:05:24.695073 [TestCapability_RoundTrip_Net] [rapid] draw cap: 107 -# 2024/07/29 13:05:24.695073 [TestCapability_RoundTrip_Net] [rapid] draw cap: 301 -# 2024/07/29 13:05:24.695074 [TestCapability_RoundTrip_Net] [rapid] draw cap: 13 -# 2024/07/29 13:05:24.695074 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 -# 2024/07/29 13:05:24.695074 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 -# 2024/07/29 13:05:24.695075 [TestCapability_RoundTrip_Net] [rapid] draw cap: 11 -# 2024/07/29 13:05:24.695075 [TestCapability_RoundTrip_Net] [rapid] draw cap: 289 -# 2024/07/29 13:05:24.695076 [TestCapability_RoundTrip_Net] [rapid] draw cap: 19 -# 2024/07/29 13:05:24.695076 [TestCapability_RoundTrip_Net] [rapid] draw cap: 68 -# 2024/07/29 13:05:24.695077 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 -# 2024/07/29 13:05:24.695079 [TestCapability_RoundTrip_Net] [rapid] draw cap: 102 -# 2024/07/29 13:05:24.695079 [TestCapability_RoundTrip_Net] [rapid] draw cap: 10 -# 2024/07/29 13:05:24.695079 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 -# 2024/07/29 13:05:24.695080 [TestCapability_RoundTrip_Net] [rapid] draw cap: 6 -# 2024/07/29 13:05:24.695080 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 -# 2024/07/29 13:05:24.695080 [TestCapability_RoundTrip_Net] [rapid] draw cap: 14 -# 2024/07/29 13:05:24.695081 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 -# 2024/07/29 13:05:24.695081 [TestCapability_RoundTrip_Net] [rapid] draw cap: 15 -# 2024/07/29 13:05:24.695081 [TestCapability_RoundTrip_Net] [rapid] draw cap: 54 -# 2024/07/29 13:05:24.695082 [TestCapability_RoundTrip_Net] [rapid] draw cap: 7 -# 2024/07/29 13:05:24.695082 [TestCapability_RoundTrip_Net] [rapid] draw cap: 94 -# 2024/07/29 13:05:24.695083 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 -# 2024/07/29 13:05:24.695084 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 -# 2024/07/29 13:05:24.695084 [TestCapability_RoundTrip_Net] [rapid] draw cap: 5 -# 2024/07/29 13:05:24.695084 [TestCapability_RoundTrip_Net] [rapid] draw cap: 206 -# 2024/07/29 13:05:24.695085 [TestCapability_RoundTrip_Net] [rapid] draw cap: 512 -# 2024/07/29 13:05:24.695085 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 -# 2024/07/29 13:05:24.695086 [TestCapability_RoundTrip_Net] [rapid] draw cap: 78 -# 2024/07/29 13:05:24.695086 [TestCapability_RoundTrip_Net] [rapid] draw cap: 11 -# 2024/07/29 13:05:24.695086 [TestCapability_RoundTrip_Net] [rapid] draw cap: 457 -# 2024/07/29 13:05:24.695203 [TestCapability_RoundTrip_Net] -# Error Trace: /home/ricks/development/livepeer/ai/go-livepeer/core/capabilities_test.go:371 -# /home/ricks/go/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 -# /home/ricks/go/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 -# /home/ricks/go/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 -# /home/ricks/go/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:118 -# /home/ricks/development/livepeer/ai/go-livepeer/core/capabilities_test.go:356 -# Error: Not equal: -# expected: &core.Capabilities{bitstring:core.CapabilityString{0x4000008048043adf, 0x0, 0x22022000000100, 0x601001, 0x800000000, 0x20, 0x20200000090000, 0x2004001000012000, 0x1}, mandatories:core.CapabilityString{0x4000000008edef, 0x84040004010, 0x0, 0x80000004000, 0x200200000080, 0x1000000000000, 0x0, 0x200, 0x1}, version:"undefined", constraints:core.Constraints{minVersion:""}, capabilityConstraints:core.CapabilityConstraints(nil), capacities:map[core.Capability]int{0:1, 1:1, 2:1, 3:1, 4:1, 6:1, 7:1, 9:1, 11:1, 12:1, 13:1, 18:1, 27:1, 30:1, 39:1, 62:1, 136:1, 165:1, 169:1, 177:1, 181:1, 192:1, 204:1, 213:1, 214:1, 291:1, 325:1, 400:1, 403:1, 429:1, 437:1, 461:1, 464:1, 484:1, 498:1, 509:1, 512:1}, mutex:sync.Mutex{state:0, sema:0x0}} -# actual : &core.Capabilities{bitstring:core.CapabilityString{0x4000008048043adf, 0x0, 0x22022000000100, 0x601001, 0x800000000, 0x20, 0x20200000090000, 0x2004001000012000, 0x1}, mandatories:core.CapabilityString{0x4000000008edef, 0x84040004010, 0x0, 0x80000004000, 0x200200000080, 0x1000000000000, 0x0, 0x200, 0x1}, version:"undefined", constraints:core.Constraints{minVersion:""}, capabilityConstraints:core.CapabilityConstraints{}, capacities:map[core.Capability]int{0:1, 1:1, 2:1, 3:1, 4:1, 6:1, 7:1, 9:1, 11:1, 12:1, 13:1, 18:1, 27:1, 30:1, 39:1, 62:1, 136:1, 165:1, 169:1, 177:1, 181:1, 192:1, 204:1, 213:1, 214:1, 291:1, 325:1, 400:1, 403:1, 429:1, 437:1, 461:1, 464:1, 484:1, 498:1, 509:1, 512:1}, mutex:sync.Mutex{state:0, sema:0x0}} -# -# Diff: -# --- Expected -# +++ Actual -# @@ -27,3 +27,4 @@ -# }, -# - capabilityConstraints: (core.CapabilityConstraints) , -# + capabilityConstraints: (core.CapabilityConstraints) { -# + }, -# capacities: (map[core.Capability]int) (len=37) { -# Test: TestCapability_RoundTrip_Net -# 2024/07/29 13:05:24.695205 [TestCapability_RoundTrip_Net] [rapid] draw capLen: 0 -# 2024/07/29 13:05:24.695206 [TestCapability_RoundTrip_Net] [rapid] draw capLen: 88 -# 2024/07/29 13:05:24.695207 [TestCapability_RoundTrip_Net] [rapid] draw cap: 4 -# 2024/07/29 13:05:24.695207 [TestCapability_RoundTrip_Net] [rapid] draw cap: 8 -# 2024/07/29 13:05:24.695208 [TestCapability_RoundTrip_Net] [rapid] draw cap: 512 -# 2024/07/29 13:05:24.695208 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 -# 2024/07/29 13:05:24.695208 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 -# 2024/07/29 13:05:24.695209 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 -# 2024/07/29 13:05:24.695210 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 -# 2024/07/29 13:05:24.695210 [TestCapability_RoundTrip_Net] [rapid] draw cap: 79 -# 2024/07/29 13:05:24.695210 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 -# 2024/07/29 13:05:24.695211 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 -# 2024/07/29 13:05:24.695211 [TestCapability_RoundTrip_Net] [rapid] draw cap: 453 -# 2024/07/29 13:05:24.695212 [TestCapability_RoundTrip_Net] [rapid] draw cap: 232 -# 2024/07/29 13:05:24.695212 [TestCapability_RoundTrip_Net] [rapid] draw cap: 8 -# 2024/07/29 13:05:24.695212 [TestCapability_RoundTrip_Net] [rapid] draw cap: 10 -# 2024/07/29 13:05:24.695213 [TestCapability_RoundTrip_Net] [rapid] draw cap: 239 -# 2024/07/29 13:05:24.695214 [TestCapability_RoundTrip_Net] [rapid] draw cap: 20 -# 2024/07/29 13:05:24.695214 [TestCapability_RoundTrip_Net] [rapid] draw cap: 5 -# 2024/07/29 13:05:24.695215 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 -# 2024/07/29 13:05:24.695216 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 -# 2024/07/29 13:05:24.695217 [TestCapability_RoundTrip_Net] [rapid] draw cap: 265 -# 2024/07/29 13:05:24.695217 [TestCapability_RoundTrip_Net] [rapid] draw cap: 150 -# 2024/07/29 13:05:24.695218 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 -# 2024/07/29 13:05:24.695219 [TestCapability_RoundTrip_Net] [rapid] draw cap: 234 -# 2024/07/29 13:05:24.695219 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 -# 2024/07/29 13:05:24.695223 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 -# 2024/07/29 13:05:24.695223 [TestCapability_RoundTrip_Net] [rapid] draw cap: 62 -# 2024/07/29 13:05:24.695223 [TestCapability_RoundTrip_Net] [rapid] draw cap: 5 -# 2024/07/29 13:05:24.695224 [TestCapability_RoundTrip_Net] [rapid] draw cap: 512 -# 2024/07/29 13:05:24.695224 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 -# 2024/07/29 13:05:24.695224 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 -# 2024/07/29 13:05:24.695225 [TestCapability_RoundTrip_Net] [rapid] draw cap: 17 -# 2024/07/29 13:05:24.695225 [TestCapability_RoundTrip_Net] [rapid] draw cap: 5 -# 2024/07/29 13:05:24.695226 [TestCapability_RoundTrip_Net] [rapid] draw cap: 75 -# 2024/07/29 13:05:24.695226 [TestCapability_RoundTrip_Net] [rapid] draw cap: 310 -# 2024/07/29 13:05:24.695226 [TestCapability_RoundTrip_Net] [rapid] draw cap: 172 -# 2024/07/29 13:05:24.695227 [TestCapability_RoundTrip_Net] [rapid] draw cap: 24 -# 2024/07/29 13:05:24.695227 [TestCapability_RoundTrip_Net] [rapid] draw cap: 88 -# 2024/07/29 13:05:24.695228 [TestCapability_RoundTrip_Net] [rapid] draw cap: 512 -# 2024/07/29 13:05:24.695228 [TestCapability_RoundTrip_Net] [rapid] draw cap: 446 -# 2024/07/29 13:05:24.695228 [TestCapability_RoundTrip_Net] [rapid] draw cap: 7 -# 2024/07/29 13:05:24.695229 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 -# 2024/07/29 13:05:24.695229 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 -# 2024/07/29 13:05:24.695229 [TestCapability_RoundTrip_Net] [rapid] draw cap: 114 -# 2024/07/29 13:05:24.695230 [TestCapability_RoundTrip_Net] [rapid] draw cap: 6 -# 2024/07/29 13:05:24.695230 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 -# 2024/07/29 13:05:24.695230 [TestCapability_RoundTrip_Net] [rapid] draw cap: 426 -# 2024/07/29 13:05:24.695231 [TestCapability_RoundTrip_Net] [rapid] draw cap: 13 -# 2024/07/29 13:05:24.695232 [TestCapability_RoundTrip_Net] [rapid] draw cap: 13 -# 2024/07/29 13:05:24.695232 [TestCapability_RoundTrip_Net] [rapid] draw cap: 6 -# 2024/07/29 13:05:24.695233 [TestCapability_RoundTrip_Net] [rapid] draw cap: 29 -# 2024/07/29 13:05:24.695233 [TestCapability_RoundTrip_Net] [rapid] draw cap: 130 -# 2024/07/29 13:05:24.695233 [TestCapability_RoundTrip_Net] [rapid] draw cap: 15 -# 2024/07/29 13:05:24.695234 [TestCapability_RoundTrip_Net] [rapid] draw cap: 196 -# 2024/07/29 13:05:24.695234 [TestCapability_RoundTrip_Net] [rapid] draw cap: 478 -# 2024/07/29 13:05:24.695234 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 -# 2024/07/29 13:05:24.695235 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 -# 2024/07/29 13:05:24.695235 [TestCapability_RoundTrip_Net] [rapid] draw cap: 10 -# 2024/07/29 13:05:24.695236 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 -# 2024/07/29 13:05:24.695236 [TestCapability_RoundTrip_Net] [rapid] draw cap: 406 -# 2024/07/29 13:05:24.695236 [TestCapability_RoundTrip_Net] [rapid] draw cap: 145 -# 2024/07/29 13:05:24.695237 [TestCapability_RoundTrip_Net] [rapid] draw cap: 241 -# 2024/07/29 13:05:24.695237 [TestCapability_RoundTrip_Net] [rapid] draw cap: 0 -# 2024/07/29 13:05:24.695237 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 -# 2024/07/29 13:05:24.695238 [TestCapability_RoundTrip_Net] [rapid] draw cap: 5 -# 2024/07/29 13:05:24.695238 [TestCapability_RoundTrip_Net] [rapid] draw cap: 229 -# 2024/07/29 13:05:24.695239 [TestCapability_RoundTrip_Net] [rapid] draw cap: 47 -# 2024/07/29 13:05:24.695239 [TestCapability_RoundTrip_Net] [rapid] draw cap: 512 -# 2024/07/29 13:05:24.695240 [TestCapability_RoundTrip_Net] [rapid] draw cap: 5 -# 2024/07/29 13:05:24.695240 [TestCapability_RoundTrip_Net] [rapid] draw cap: 270 -# 2024/07/29 13:05:24.695241 [TestCapability_RoundTrip_Net] [rapid] draw cap: 148 -# 2024/07/29 13:05:24.695241 [TestCapability_RoundTrip_Net] [rapid] draw cap: 292 -# 2024/07/29 13:05:24.695241 [TestCapability_RoundTrip_Net] [rapid] draw cap: 146 -# 2024/07/29 13:05:24.695242 [TestCapability_RoundTrip_Net] [rapid] draw cap: 411 -# 2024/07/29 13:05:24.695242 [TestCapability_RoundTrip_Net] [rapid] draw cap: 49 -# 2024/07/29 13:05:24.695242 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 -# 2024/07/29 13:05:24.695243 [TestCapability_RoundTrip_Net] [rapid] draw cap: 132 -# 2024/07/29 13:05:24.695243 [TestCapability_RoundTrip_Net] [rapid] draw cap: 9 -# 2024/07/29 13:05:24.695243 [TestCapability_RoundTrip_Net] [rapid] draw cap: 193 -# 2024/07/29 13:05:24.695244 [TestCapability_RoundTrip_Net] [rapid] draw cap: 510 -# 2024/07/29 13:05:24.695244 [TestCapability_RoundTrip_Net] [rapid] draw cap: 3 -# 2024/07/29 13:05:24.695245 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 -# 2024/07/29 13:05:24.695245 [TestCapability_RoundTrip_Net] [rapid] draw cap: 22 -# 2024/07/29 13:05:24.695245 [TestCapability_RoundTrip_Net] [rapid] draw cap: 2 -# 2024/07/29 13:05:24.695246 [TestCapability_RoundTrip_Net] [rapid] draw cap: 136 -# 2024/07/29 13:05:24.695246 [TestCapability_RoundTrip_Net] [rapid] draw cap: 408 -# 2024/07/29 13:05:24.695246 [TestCapability_RoundTrip_Net] [rapid] draw cap: 11 -# 2024/07/29 13:05:24.695247 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 -# 2024/07/29 13:05:24.695247 [TestCapability_RoundTrip_Net] [rapid] draw cap: 1 -# -v0.4.8#8355047278167994326 -0x1ffe553afdc0f7 -0xf45b3a3026fb1 -0x36 -0x19e8d3757890a9 -0x1ece3789377bd6 -0x254 -0x205 -0x1d0 -0xc696ee1730946 -0xa5bfc6000ae4 -0x1 -0x1e767e042e2f9d -0x176d810cfc0f2b -0x267 -0x1cd -0x2a57c0b86b7d2 -0x1e916bccdef442 -0x1fd -0xcf6dc68c6ae6f -0x17398c89c02dec -0x1ad -0x131c5a873b987b -0x157a8063db5cc8 -0x27 -0x1987e962a4ed1 -0x12edc22943d81 -0x0 -0x163376461fd54d -0x9ce4ce56c30ba -0x9 -0x1e5486cb3b6174 -0x16c0f7f21eb8cb -0x123 -0xf0a4d4dd1e518 -0x1ac2347bba71a3 -0x4 -0x169e45fe17b9c4 -0x5c8baba4436ce -0x1 -0x1fa586fec7e96b -0x120720f4f21290 -0x1b -0x4fb6259a712e2 -0x37f35a9300112 -0x3 -0x1935b725e9e96 -0x187a601d1528ca -0xd6 -0x7913ebe9dc13a -0x142e0f585b2dac -0xc0 -0x196f40656a41ec -0x3af286f273a5d -0x0 -0x1a2c3ac100cc8e -0x1a36899482617e -0x3a3 -0x1e4 -0x1b6b16ee1d7b0 -0x1cbffa9417a94e -0xa5 -0x6edec3d67143c -0x127c8c567c73c4 -0xb1 -0x188f8cfc3194f8 -0x15a4da498b416b -0xd5 -0x1ac94151d50f1b -0x1cbdfcfdc9aba8 -0x12 -0x4424d7c0cfd27 -0xc2c679bb08e83 -0x3 -0x13a41a68b5e7b5 -0xe824a9c93fb79 -0x9 -0x104eae942c2d3a -0x8401f4f6add60 -0x2 -0x1a85ae65d696b5 -0xd9a97b75d45a0 -0xc -0x1dcdacf4d1f538 -0x5b79db0b9ce2e -0x1 -0x105690b2cc5155 -0x160e817f276ba9 -0x1b5 -0x1aef64c8f37d26 -0xaf0d1bd20e7f0 -0xb -0x124d67659fe95 -0xef30a4d402397 -0x27 -0xea3815f25e942 -0xd3dd513625b5f -0x1e -0xa34a45d0353c0 -0x181b4b8c96b635 -0x2cf -0x390 -0xc -0x1249d6d7171e3e -0x18466e9bb7d281 -0x3c9 -0x358 -0x145 -0x99299469331c0 -0x1225c985e1f9d7 -0xcc -0x8ae4b0b2d445c -0xa0005e7ee8688 -0xd -0x156193b8049849 -0x1beecbb840472f -0x3c1 -0x318 -0x2ab -0x193 -0x14816d4d091040 -0x1e18e4ad3afb90 -0x312 -0x1f2 -0x16d98afc38a5eb -0x1ffb0857cf3f4e -0xffffffffffffffff -0x63bc8e9d1b67c -0x670439aa47cba -0x3 -0x7d862db5053c3 -0x908d39c551ddc -0x0 -0x1e5beb91ed0b7 -0x11cff49a778ef7 -0x88 -0xc149b27cdeaff -0x5ae64d015a7f1 -0x2 -0x3f1601a4584c9 -0x12b7466721c2e3 -0xa9 -0x141e20558b5b71 -0x1b482d7eb0eab -0x1 -0xfe9be41b1a7d1 -0x11fbe041d5e3a2 -0x3e -0xc05c575caa3e2 -0x12fd3f55f92749 -0xb5 -0xfef307a80649c -0x694d773b6e563 -0x0 -0x1284e54af89718 -0x8f1a506967360 -0x6 -0x1eecbbac2653a2 -0xafb39c58bdb30 -0xc -0x1397764b96e3d7 -0x1fcf6c00def779 -0xffffffffffffffff -0xd943ace824438 -0x1234ba12e2b5d9 -0xb1 -0xc079ec12eaad5 -0x7e0bf0514df4a -0x7 -0x151b5694ff1a29 -0x3699052aae4ed -0x0 -0x160ee2a3299343 -0x94725373b3b5e -0x0 -0x19762e14c2a5a5 -0x1d3d3495650207 -0x190 -0x16cfffcbf32568 -0xe62e37e3841db -0x2a -0x11079c4e5b4840 -0x54fc022a5fc28 -0x0 -0x12cb7927ef68c -0xab6a464b9bb44 -0x6 -0x1396451ba612f3 -0x15955083a4f426 -0x2c4 -0x3c2 -0x170 -0x8ad02f4160198 -0x1f2e292b02fc83 -0xffffffffffffffff -0x947f2842e50d6 -0xeaa1a5622e3a4 -0xa -0x1a1704c8278973 -0x12a05ed66f4c0a -0xeb -0x1114fdc6207cd2 -0x3bb4cfcac042b -0x1 -0x1b3b74b3d01456 -0x531f320b2531b -0x1 -0x197e4b75069d1c -0x4d8176640b92e -0x3 -0x1b012144f0fa51 -0x10fae59448f46a -0x8 -0x10539663fa4035 -0x164c0b334cded1 -0x37c -0x107 -0x125a9a06e7bba5 -0xb9b43c76717b -0x1 -0x1cf2aeb6603cb3 -0x149038ed5d9d11 -0x6b -0xf173ebe9135e5 -0x1811a49e35de09 -0x12d -0xb813b577702a -0xafe2049d3f562 -0xd -0x1ba2b7b064c255 -0x20607cec5b805 -0x1 -0x16c2154199ef32 -0x23f3c71d80c9b -0x1 -0x1106d63649cf19 -0xa2b99bca8e7ac -0xb -0xa6d34ffecd0a5 -0x159f02878e5f6d -0x2ea -0x2ba -0x31b -0x121 -0x17762d304d229e -0x1ae07bc79e4410 -0x13 -0x16520166b2318f -0x18b9eed5884993 -0x39e -0x44 -0x156a3d2e785604 -0x6de195e62fd9c -0x1 -0x357a4cb6e9746 -0x1ec3aa7ce66580 -0x2cd -0x66 -0x17ac30ce8ec31b -0x9497852492b84 -0xa -0x18ac792a689485 -0x3266d37f6731d -0x0 -0x158865b1697fc3 -0xb5ea2b426535e -0x6 -0x1db03d2d540db2 -0x48084744eebcc -0x1 -0xf03b281d81e80 -0x10b4af861d654f -0xe -0x1b54ecc34b53e2 -0x6106d8d99c88d -0x3 -0xb165bcd6cecde -0xf3ff59d242ab5 -0xf -0x340e867d1532d -0x1130568709d9e7 -0x36 -0x12d1ffb3754d5b -0xa84458eda2d0e -0x7 -0x1a64f2673adbc7 -0x18822fb55da60d -0x30c -0x230 -0x393 -0x5e -0x1249caf2fa4cc7 -0x1090841a6a3c7 -0x1 -0xbbab3008bcd4b -0x5e0cf2416db3c -0x1 -0x1ae89840ef2766 -0x8c200ba4313b8 -0x5 -0x1fee6a270f448d -0x11d30f6fb09a46 -0xce -0xe8381b8eda998 -0x1f1e4a7c6aa3ec -0xffffffffffffffff -0xf6462d88f7320 -0x49d09a27bb9f3 -0x2 -0x416e964435cca -0x1e1ff6f6b79794 -0x367 -0x2a1 -0x4e -0x2c8f3bf759265 -0xc76dc2ff2479b -0xb -0x1b474ddd26012d -0x18213ba0d029a5 -0x2c7 -0x21e -0x3cd -0x2ed -0x1c9 -0x12eeb282e67311 -0x70f801cad570 -0x0 -0xe7431464d3755 -0x1033f1de5f67a0 -0x58 -0x1535a55a6d00d -0xeec21f4e4462d -0x4 -0x74c5911718c5f -0xcde3a5429d3bf -0x8 -0x11b45fdd094ebb -0x1f167cee04ae61 -0xffffffffffffffff -0x17a28518b52445 -0x3321f47533362 -0x0 -0xdd01a125c4a4a -0x6a89a8d9ab90b -0x3 -0xe1049754f4420 -0x94c9cca847000 -0x2 -0x163568c0c286a3 -0x320875f898519 -0x1 -0xd65f1429c43f8 -0x19d7d707113edd -0x20f -0x2c9 -0x4f -0x860cd5aab781b -0x39b3c9375f97d -0x0 -0x2333e3d7ca98d -0x17a6d7616fae5 -0x0 -0x1be31c31fa1b65 -0x1e9cf0eff97714 -0x3b2 -0x1c5 -0x228d962252c1 -0x1da1342813d162 -0xe8 -0xcb52be9526514 -0xbe7715933fd26 -0x8 -0x8f1a4b72402b3 -0xd29274e087de2 -0xa -0x16eaf729826089 -0x18c0985d643ec9 -0x3eb -0x3af -0xef -0x771448beb2b60 -0xca48c76e6a43b -0x14 -0x12e8d395659fca -0x6962f0ecdd7eb -0x5 -0x1b5b9a7947347f -0x111eac80e7a6a -0x0 -0xa2f9593345b1a -0x23ab2574ece76 -0x0 -0x52bdb9f49038 -0x13c1e91774246c -0x109 -0x11678fb01f0e94 -0x1eed50c572ce61 -0x276 -0x96 -0x85293dd2b81d9 -0x46f5a55a32f21 -0x2 -0x8ea156b2403b8 -0x130c376c244b7a -0xea -0x593dbf28c8642 -0x48fb750754798 -0x0 -0x218b6e19d2ce7 -0x28667e2348614 -0x0 -0xb47c81a484822 -0xe7763abc2530e -0x3e -0x11621e6432f25e -0xb6e51a3c7540d -0x5 -0x15a8205db3e4cf -0x1f528a70225267 -0xffffffffffffffff -0x7f1a01494e0e2 -0x64a97840199f5 -0x0 -0xe8a7648610a60 -0x7814e573acea1 -0x2 -0x9f52042f72a1f -0xceeb054adc0f0 -0x11 -0x1f8e17539ad7cf -0x726edc3a875f6 -0x5 -0x14704edc0975f7 -0x118b2f49f75478 -0x4b -0x1bc318c2f02b8e -0x158d0ef586448e -0x2a1 -0x381 -0x136 -0x10bd503772dde5 -0x11f23f5e7b3f97 -0xac -0x184032fc1d0289 -0x112711dd817d83 -0x18 -0x273b20abf615c -0x1a1f72d887b0c2 -0x221 -0x58 -0x75447e86e9506 -0x1f5077c5158403 -0xffffffffffffffff -0x1484c44b895db2 -0x1a7a4cd63fe6fd -0x2d8 -0x3a7 -0x1be -0x49d80aad06826 -0xb75b3c47ca70b -0x7 -0x17b50ef6547c08 -0x61a34e6ccfe60 -0x3 -0x17c0e7eed36e10 -0x6a094e5ea11f2 -0x3 -0xe6e5d341e0f9f -0x18519b4701487e -0x3fb -0x72 -0xf64409ba1a52b -0x7c54c8de9c737 -0x6 -0x3bd3309d85dfa -0xd7c26e0c06e7f -0x2 -0x10a3874da0fe24 -0x17ee1a5fd6e736 -0x367 -0x25e -0x1aa -0xedea7935cd0ab -0xbce43cf730e22 -0xd -0x189100661f2b80 -0xbf03d117400cd -0xd -0x1940aba500afec -0xf0331a24e3663 -0x6 -0xbf51d6f2c634e -0x135a22a6c9173a -0x1d -0x6082f4e73edc4 -0x1e76c6b2e83524 -0x82 -0x23d8833c0cfd1 -0xad6c5e891d077 -0xf -0x1395753897997a -0x12a341871f9b19 -0xc4 -0x1af8256ea1b399 -0x1e023f88c2f3d1 -0x21f -0x1de -0x15e381df69a2fb -0x36dbfaad03710 -0x0 -0x1636516372e7b1 -0x2f1ee347aed69 -0x1 -0xb3da16c4564fc -0xc3cbf6936777d -0xa -0xa747baaa63b10 -0x5fd09cf3fd7ae -0x3 -0x841b987647c93 -0x14ee7c5e1c4aec -0x378 -0x310 -0x196 -0x86138cb44b38e -0x11ce7c42224c97 -0x91 -0x199672e5f8fe30 -0x19f254a45c1ad8 -0xf1 -0x93cbdc9747fab -0xd9f690e1b64d0 -0x0 -0x1ab1e9f7ba4a -0x59e57af077bea -0x2 -0x1c885e18521117 -0x81245406d707c -0x5 -0x1c09c2947a1067 -0x13215075ebb743 -0xe5 -0x1fc0b792b6c8ce -0xe9884cf04721d -0x2f -0x144617c620a489 -0x1f497e7115f5b4 -0xffffffffffffffff -0x1ae47ced5ad3a3 -0xa2c6fbd42a883 -0x5 -0xb9e9253823be -0x1bd67da246b7a6 -0x10e -0x1bb16c7f546078 -0x1edc5424565bbe -0x21c -0x305 -0x309 -0x94 -0x72cc716d0d736 -0x1e7c78aceae3e8 -0x124 -0xd0bf7d6644188 -0x133d77cb1a83e7 -0x92 -0x139da06e6cbf55 -0x1ca3de51b16a13 -0x19b -0x194a80ee239485 -0x1704d2bf665acd -0x3f4 -0x229 -0x31 -0x30bb2986d48e0 -0x6dad9af00bae1 -0x3 -0x143d3ccf72cc6a -0x1272257645e054 -0x84 -0x1124a37048f083 -0x1084ce7caad86a -0x9 -0x1cea54a09c4bb5 -0x1ab6eac00ea1cc -0xc1 -0x13c855d1666b97 -0x1da18afcac568a -0x273 -0x1fe -0x134dcf198d08d5 -0x56d7aa877e65b -0x3 -0x19c1af5ea21ea0 -0xeaca78e7822 -0x1 -0x807e8630dc7ed -0xdb91f876d1fe5 -0x16 -0xdd5c5f42f155 -0x129a02e8915eef -0x2 -0x365fdd52f9d57 -0x15660795ef0ed4 -0x217 -0x88 -0xdc1d746a07de2 -0x1e4a5c76a57b57 -0x198 -0x13b94df18444f1 -0xbb1f76a846da0 -0xb -0x17bb74a9f969e6 -0x42d5523f10d7a -0x1 -0x105046a138d532 -0x30621e8844265 -0x1 \ No newline at end of file diff --git a/core/transcoder_test.go b/core/transcoder_test.go index 25e19c581..8b8061ffc 100644 --- a/core/transcoder_test.go +++ b/core/transcoder_test.go @@ -32,10 +32,10 @@ func TestLocalTranscoder(t *testing.T) { if len(res.Segments) != len(videoProfiles) { t.Error("Mismatched results") } - if Over1Pct(len(res.Segments[0].Data), 585620) { + if Over1Pct(len(res.Segments[0].Data), 522264) { t.Errorf("Wrong data %v", len(res.Segments[0].Data)) } - if Over1Pct(len(res.Segments[1].Data), 813100) { + if Over1Pct(len(res.Segments[1].Data), 715528) { t.Errorf("Wrong data %v", len(res.Segments[1].Data)) } } diff --git a/discovery/db_discovery.go b/discovery/db_discovery.go index 5c9905bdd..caf210666 100644 --- a/discovery/db_discovery.go +++ b/discovery/db_discovery.go @@ -16,6 +16,7 @@ import ( lpTypes "github.com/livepeer/go-livepeer/eth/types" "github.com/livepeer/go-livepeer/net" "github.com/livepeer/go-livepeer/pm" + "github.com/livepeer/go-livepeer/server" "github.com/golang/glog" ) @@ -70,6 +71,7 @@ func NewDBOrchestratorPoolCache(ctx context.Context, node *core.LivepeerNode, rm func (dbo *DBOrchestratorPoolCache) getURLs() ([]*url.URL, error) { orchs, err := dbo.store.SelectOrchs( &common.DBOrchFilter{ + MaxPrice: server.BroadcastCfg.MaxPrice(), CurrentRound: dbo.rm.LastInitializedRound(), UpdatedLastDay: true, }, @@ -118,7 +120,8 @@ func (dbo *DBOrchestratorPoolCache) GetOrchestrators(ctx context.Context, numOrc return false } - // check if O has a valid price + // check if O's price is below B's max price + maxPrice := server.BroadcastCfg.MaxPrice() price, err := common.RatPriceInfo(info.PriceInfo) if err != nil { clog.V(common.DEBUG).Infof(ctx, "invalid price info orch=%v err=%q", info.GetTranscoder(), err) @@ -128,8 +131,12 @@ func (dbo *DBOrchestratorPoolCache) GetOrchestrators(ctx context.Context, numOrc clog.V(common.DEBUG).Infof(ctx, "no price info received for orch=%v", info.GetTranscoder()) return false } - if price.Sign() < 0 { - clog.V(common.DEBUG).Infof(ctx, "invalid price received for orch=%v price=%v", info.GetTranscoder(), price.RatString()) + if maxPrice != nil && price.Cmp(maxPrice) > 0 { + clog.V(common.DEBUG).Infof(ctx, "orchestrator's price is too high orch=%v price=%v wei/pixel maxPrice=%v wei/pixel", + info.GetTranscoder(), + price.FloatString(3), + maxPrice.FloatString(3), + ) return false } return true @@ -147,6 +154,7 @@ func (dbo *DBOrchestratorPoolCache) GetOrchestrators(ctx context.Context, numOrc func (dbo *DBOrchestratorPoolCache) Size() int { count, _ := dbo.store.OrchCount( &common.DBOrchFilter{ + MaxPrice: server.BroadcastCfg.MaxPrice(), CurrentRound: dbo.rm.LastInitializedRound(), UpdatedLastDay: true, }, diff --git a/discovery/discovery_test.go b/discovery/discovery_test.go index 33e20e5b7..c52ce7ce6 100644 --- a/discovery/discovery_test.go +++ b/discovery/discovery_test.go @@ -608,11 +608,11 @@ func TestNewOrchestratorPoolWithPred_TestPredicate(t *testing.T) { assert.True(t, pool.pred(oInfo)) // Set server.BroadcastCfg.maxPrice higher than PriceInfo , should return true - server.BroadcastCfg.SetMaxPrice(core.NewFixedPrice(big.NewRat(10, 1))) + server.BroadcastCfg.SetMaxPrice(big.NewRat(10, 1)) assert.True(t, pool.pred(oInfo)) // Set MaxBroadcastPrice lower than PriceInfo, should return false - server.BroadcastCfg.SetMaxPrice(core.NewFixedPrice(big.NewRat(1, 1))) + server.BroadcastCfg.SetMaxPrice(big.NewRat(1, 1)) assert.False(t, pool.pred(oInfo)) // PixelsPerUnit is 0 , return false @@ -620,7 +620,7 @@ func TestNewOrchestratorPoolWithPred_TestPredicate(t *testing.T) { assert.False(t, pool.pred(oInfo)) } -func TestCachedPool_AllOrchestratorsTooExpensive_ReturnsAllOrchestrators(t *testing.T) { +func TestCachedPool_AllOrchestratorsTooExpensive_ReturnsEmptyList(t *testing.T) { // Test setup expPriceInfo := &net.PriceInfo{ PricePerUnit: 999, @@ -629,7 +629,7 @@ func TestCachedPool_AllOrchestratorsTooExpensive_ReturnsAllOrchestrators(t *test expTranscoder := "transcoderFromTest" expPricePerPixel, _ := common.PriceToFixed(big.NewRat(999, 1)) - server.BroadcastCfg.SetMaxPrice(core.NewFixedPrice(big.NewRat(1, 1))) + server.BroadcastCfg.SetMaxPrice(big.NewRat(1, 1)) gmp := runtime.GOMAXPROCS(50) defer runtime.GOMAXPROCS(gmp) var mu sync.Mutex @@ -698,15 +698,14 @@ func TestCachedPool_AllOrchestratorsTooExpensive_ReturnsAllOrchestrators(t *test } // check size - assert.Equal(50, pool.Size()) + assert.Equal(0, pool.Size()) urls := pool.GetInfos() - assert.Len(urls, 50) - + assert.Len(urls, 0) infos, err := pool.GetOrchestrators(context.TODO(), len(addresses), newStubSuspender(), newStubCapabilities(), common.ScoreAtLeast(0)) assert.Nil(err, "Should not be error") - assert.Len(infos, 50) + assert.Len(infos, 0) } func TestCachedPool_GetOrchestrators_MaxBroadcastPriceNotSet(t *testing.T) { @@ -824,7 +823,7 @@ func TestCachedPool_N_OrchestratorsGoodPricing_ReturnsNOrchestrators(t *testing. }, } - server.BroadcastCfg.SetMaxPrice(core.NewFixedPrice(big.NewRat(10, 1))) + server.BroadcastCfg.SetMaxPrice(big.NewRat(10, 1)) gmp := runtime.GOMAXPROCS(50) defer runtime.GOMAXPROCS(gmp) var mu sync.Mutex @@ -900,27 +899,22 @@ func TestCachedPool_N_OrchestratorsGoodPricing_ReturnsNOrchestrators(t *testing. assert.Contains(testOrchs[25:], toOrchTest(o.EthereumAddr, o.ServiceURI, o.PricePerPixel)) } - // check pool returns all Os, not filtering by max price - assert.Equal(50, pool.Size()) + // check size + assert.Equal(25, pool.Size()) infos := pool.GetInfos() - assert.Len(infos, 50) + assert.Len(infos, 25) for _, info := range infos { - assert.Contains(addresses, info.URL.String()) + assert.Contains(addresses[25:], info.URL.String()) } oinfos, err := pool.GetOrchestrators(context.TODO(), len(orchestrators), newStubSuspender(), newStubCapabilities(), common.ScoreAtLeast(0)) assert.Nil(err, "Should not be error") - assert.Len(oinfos, 50) - - seenAddrs := make(map[string]bool) + assert.Len(oinfos, 25) for _, info := range oinfos { - addr := info.LocalInfo.URL.String() - assert.Contains(addresses, addr) - seenAddrs[addr] = true + assert.Equal(info.RemoteInfo.Transcoder, "goodPriceTranscoder") } - assert.Len(seenAddrs, 50) } func TestCachedPool_GetOrchestrators_TicketParamsValidation(t *testing.T) { diff --git a/eth/contracts/chainlink/AggregatorV3Interface.abi b/eth/contracts/chainlink/AggregatorV3Interface.abi deleted file mode 100644 index 106c4a7bc..000000000 --- a/eth/contracts/chainlink/AggregatorV3Interface.abi +++ /dev/null @@ -1 +0,0 @@ -[{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"description","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint80","name":"_roundId","type":"uint80"}],"name":"getRoundData","outputs":[{"internalType":"uint80","name":"roundId","type":"uint80"},{"internalType":"int256","name":"answer","type":"int256"},{"internalType":"uint256","name":"startedAt","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"},{"internalType":"uint80","name":"answeredInRound","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestRoundData","outputs":[{"internalType":"uint80","name":"roundId","type":"uint80"},{"internalType":"int256","name":"answer","type":"int256"},{"internalType":"uint256","name":"startedAt","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"},{"internalType":"uint80","name":"answeredInRound","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}] diff --git a/eth/contracts/chainlink/AggregatorV3Interface.go b/eth/contracts/chainlink/AggregatorV3Interface.go deleted file mode 100644 index 2b0c1c958..000000000 --- a/eth/contracts/chainlink/AggregatorV3Interface.go +++ /dev/null @@ -1,394 +0,0 @@ -// Code generated - DO NOT EDIT. -// This file is a generated binding and any manual changes will be lost. - -package chainlink - -import ( - "errors" - "math/big" - "strings" - - ethereum "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/event" -) - -// Reference imports to suppress errors if they are not otherwise used. -var ( - _ = errors.New - _ = big.NewInt - _ = strings.NewReader - _ = ethereum.NotFound - _ = bind.Bind - _ = common.Big1 - _ = types.BloomLookup - _ = event.NewSubscription - _ = abi.ConvertType -) - -// AggregatorV3InterfaceMetaData contains all meta data concerning the AggregatorV3Interface contract. -var AggregatorV3InterfaceMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"description\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint80\",\"name\":\"_roundId\",\"type\":\"uint80\"}],\"name\":\"getRoundData\",\"outputs\":[{\"internalType\":\"uint80\",\"name\":\"roundId\",\"type\":\"uint80\"},{\"internalType\":\"int256\",\"name\":\"answer\",\"type\":\"int256\"},{\"internalType\":\"uint256\",\"name\":\"startedAt\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"updatedAt\",\"type\":\"uint256\"},{\"internalType\":\"uint80\",\"name\":\"answeredInRound\",\"type\":\"uint80\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestRoundData\",\"outputs\":[{\"internalType\":\"uint80\",\"name\":\"roundId\",\"type\":\"uint80\"},{\"internalType\":\"int256\",\"name\":\"answer\",\"type\":\"int256\"},{\"internalType\":\"uint256\",\"name\":\"startedAt\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"updatedAt\",\"type\":\"uint256\"},{\"internalType\":\"uint80\",\"name\":\"answeredInRound\",\"type\":\"uint80\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"version\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", -} - -// AggregatorV3InterfaceABI is the input ABI used to generate the binding from. -// Deprecated: Use AggregatorV3InterfaceMetaData.ABI instead. -var AggregatorV3InterfaceABI = AggregatorV3InterfaceMetaData.ABI - -// AggregatorV3Interface is an auto generated Go binding around an Ethereum contract. -type AggregatorV3Interface struct { - AggregatorV3InterfaceCaller // Read-only binding to the contract - AggregatorV3InterfaceTransactor // Write-only binding to the contract - AggregatorV3InterfaceFilterer // Log filterer for contract events -} - -// AggregatorV3InterfaceCaller is an auto generated read-only Go binding around an Ethereum contract. -type AggregatorV3InterfaceCaller struct { - contract *bind.BoundContract // Generic contract wrapper for the low level calls -} - -// AggregatorV3InterfaceTransactor is an auto generated write-only Go binding around an Ethereum contract. -type AggregatorV3InterfaceTransactor struct { - contract *bind.BoundContract // Generic contract wrapper for the low level calls -} - -// AggregatorV3InterfaceFilterer is an auto generated log filtering Go binding around an Ethereum contract events. -type AggregatorV3InterfaceFilterer struct { - contract *bind.BoundContract // Generic contract wrapper for the low level calls -} - -// AggregatorV3InterfaceSession is an auto generated Go binding around an Ethereum contract, -// with pre-set call and transact options. -type AggregatorV3InterfaceSession struct { - Contract *AggregatorV3Interface // Generic contract binding to set the session for - CallOpts bind.CallOpts // Call options to use throughout this session - TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session -} - -// AggregatorV3InterfaceCallerSession is an auto generated read-only Go binding around an Ethereum contract, -// with pre-set call options. -type AggregatorV3InterfaceCallerSession struct { - Contract *AggregatorV3InterfaceCaller // Generic contract caller binding to set the session for - CallOpts bind.CallOpts // Call options to use throughout this session -} - -// AggregatorV3InterfaceTransactorSession is an auto generated write-only Go binding around an Ethereum contract, -// with pre-set transact options. -type AggregatorV3InterfaceTransactorSession struct { - Contract *AggregatorV3InterfaceTransactor // Generic contract transactor binding to set the session for - TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session -} - -// AggregatorV3InterfaceRaw is an auto generated low-level Go binding around an Ethereum contract. -type AggregatorV3InterfaceRaw struct { - Contract *AggregatorV3Interface // Generic contract binding to access the raw methods on -} - -// AggregatorV3InterfaceCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. -type AggregatorV3InterfaceCallerRaw struct { - Contract *AggregatorV3InterfaceCaller // Generic read-only contract binding to access the raw methods on -} - -// AggregatorV3InterfaceTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. -type AggregatorV3InterfaceTransactorRaw struct { - Contract *AggregatorV3InterfaceTransactor // Generic write-only contract binding to access the raw methods on -} - -// NewAggregatorV3Interface creates a new instance of AggregatorV3Interface, bound to a specific deployed contract. -func NewAggregatorV3Interface(address common.Address, backend bind.ContractBackend) (*AggregatorV3Interface, error) { - contract, err := bindAggregatorV3Interface(address, backend, backend, backend) - if err != nil { - return nil, err - } - return &AggregatorV3Interface{AggregatorV3InterfaceCaller: AggregatorV3InterfaceCaller{contract: contract}, AggregatorV3InterfaceTransactor: AggregatorV3InterfaceTransactor{contract: contract}, AggregatorV3InterfaceFilterer: AggregatorV3InterfaceFilterer{contract: contract}}, nil -} - -// NewAggregatorV3InterfaceCaller creates a new read-only instance of AggregatorV3Interface, bound to a specific deployed contract. -func NewAggregatorV3InterfaceCaller(address common.Address, caller bind.ContractCaller) (*AggregatorV3InterfaceCaller, error) { - contract, err := bindAggregatorV3Interface(address, caller, nil, nil) - if err != nil { - return nil, err - } - return &AggregatorV3InterfaceCaller{contract: contract}, nil -} - -// NewAggregatorV3InterfaceTransactor creates a new write-only instance of AggregatorV3Interface, bound to a specific deployed contract. -func NewAggregatorV3InterfaceTransactor(address common.Address, transactor bind.ContractTransactor) (*AggregatorV3InterfaceTransactor, error) { - contract, err := bindAggregatorV3Interface(address, nil, transactor, nil) - if err != nil { - return nil, err - } - return &AggregatorV3InterfaceTransactor{contract: contract}, nil -} - -// NewAggregatorV3InterfaceFilterer creates a new log filterer instance of AggregatorV3Interface, bound to a specific deployed contract. -func NewAggregatorV3InterfaceFilterer(address common.Address, filterer bind.ContractFilterer) (*AggregatorV3InterfaceFilterer, error) { - contract, err := bindAggregatorV3Interface(address, nil, nil, filterer) - if err != nil { - return nil, err - } - return &AggregatorV3InterfaceFilterer{contract: contract}, nil -} - -// bindAggregatorV3Interface binds a generic wrapper to an already deployed contract. -func bindAggregatorV3Interface(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { - parsed, err := AggregatorV3InterfaceMetaData.GetAbi() - if err != nil { - return nil, err - } - return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil -} - -// Call invokes the (constant) contract method with params as input values and -// sets the output to result. The result type might be a single field for simple -// returns, a slice of interfaces for anonymous returns and a struct for named -// returns. -func (_AggregatorV3Interface *AggregatorV3InterfaceRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { - return _AggregatorV3Interface.Contract.AggregatorV3InterfaceCaller.contract.Call(opts, result, method, params...) -} - -// Transfer initiates a plain transaction to move funds to the contract, calling -// its default method if one is available. -func (_AggregatorV3Interface *AggregatorV3InterfaceRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { - return _AggregatorV3Interface.Contract.AggregatorV3InterfaceTransactor.contract.Transfer(opts) -} - -// Transact invokes the (paid) contract method with params as input values. -func (_AggregatorV3Interface *AggregatorV3InterfaceRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { - return _AggregatorV3Interface.Contract.AggregatorV3InterfaceTransactor.contract.Transact(opts, method, params...) -} - -// Call invokes the (constant) contract method with params as input values and -// sets the output to result. The result type might be a single field for simple -// returns, a slice of interfaces for anonymous returns and a struct for named -// returns. -func (_AggregatorV3Interface *AggregatorV3InterfaceCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { - return _AggregatorV3Interface.Contract.contract.Call(opts, result, method, params...) -} - -// Transfer initiates a plain transaction to move funds to the contract, calling -// its default method if one is available. -func (_AggregatorV3Interface *AggregatorV3InterfaceTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { - return _AggregatorV3Interface.Contract.contract.Transfer(opts) -} - -// Transact invokes the (paid) contract method with params as input values. -func (_AggregatorV3Interface *AggregatorV3InterfaceTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { - return _AggregatorV3Interface.Contract.contract.Transact(opts, method, params...) -} - -// Decimals is a free data retrieval call binding the contract method 0x313ce567. -// -// Solidity: function decimals() view returns(uint8) -func (_AggregatorV3Interface *AggregatorV3InterfaceCaller) Decimals(opts *bind.CallOpts) (uint8, error) { - var out []interface{} - err := _AggregatorV3Interface.contract.Call(opts, &out, "decimals") - - if err != nil { - return *new(uint8), err - } - - out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) - - return out0, err - -} - -// Decimals is a free data retrieval call binding the contract method 0x313ce567. -// -// Solidity: function decimals() view returns(uint8) -func (_AggregatorV3Interface *AggregatorV3InterfaceSession) Decimals() (uint8, error) { - return _AggregatorV3Interface.Contract.Decimals(&_AggregatorV3Interface.CallOpts) -} - -// Decimals is a free data retrieval call binding the contract method 0x313ce567. -// -// Solidity: function decimals() view returns(uint8) -func (_AggregatorV3Interface *AggregatorV3InterfaceCallerSession) Decimals() (uint8, error) { - return _AggregatorV3Interface.Contract.Decimals(&_AggregatorV3Interface.CallOpts) -} - -// Description is a free data retrieval call binding the contract method 0x7284e416. -// -// Solidity: function description() view returns(string) -func (_AggregatorV3Interface *AggregatorV3InterfaceCaller) Description(opts *bind.CallOpts) (string, error) { - var out []interface{} - err := _AggregatorV3Interface.contract.Call(opts, &out, "description") - - if err != nil { - return *new(string), err - } - - out0 := *abi.ConvertType(out[0], new(string)).(*string) - - return out0, err - -} - -// Description is a free data retrieval call binding the contract method 0x7284e416. -// -// Solidity: function description() view returns(string) -func (_AggregatorV3Interface *AggregatorV3InterfaceSession) Description() (string, error) { - return _AggregatorV3Interface.Contract.Description(&_AggregatorV3Interface.CallOpts) -} - -// Description is a free data retrieval call binding the contract method 0x7284e416. -// -// Solidity: function description() view returns(string) -func (_AggregatorV3Interface *AggregatorV3InterfaceCallerSession) Description() (string, error) { - return _AggregatorV3Interface.Contract.Description(&_AggregatorV3Interface.CallOpts) -} - -// GetRoundData is a free data retrieval call binding the contract method 0x9a6fc8f5. -// -// Solidity: function getRoundData(uint80 _roundId) view returns(uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) -func (_AggregatorV3Interface *AggregatorV3InterfaceCaller) GetRoundData(opts *bind.CallOpts, _roundId *big.Int) (struct { - RoundId *big.Int - Answer *big.Int - StartedAt *big.Int - UpdatedAt *big.Int - AnsweredInRound *big.Int -}, error) { - var out []interface{} - err := _AggregatorV3Interface.contract.Call(opts, &out, "getRoundData", _roundId) - - outstruct := new(struct { - RoundId *big.Int - Answer *big.Int - StartedAt *big.Int - UpdatedAt *big.Int - AnsweredInRound *big.Int - }) - if err != nil { - return *outstruct, err - } - - outstruct.RoundId = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) - outstruct.Answer = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int) - outstruct.StartedAt = *abi.ConvertType(out[2], new(*big.Int)).(**big.Int) - outstruct.UpdatedAt = *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) - outstruct.AnsweredInRound = *abi.ConvertType(out[4], new(*big.Int)).(**big.Int) - - return *outstruct, err - -} - -// GetRoundData is a free data retrieval call binding the contract method 0x9a6fc8f5. -// -// Solidity: function getRoundData(uint80 _roundId) view returns(uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) -func (_AggregatorV3Interface *AggregatorV3InterfaceSession) GetRoundData(_roundId *big.Int) (struct { - RoundId *big.Int - Answer *big.Int - StartedAt *big.Int - UpdatedAt *big.Int - AnsweredInRound *big.Int -}, error) { - return _AggregatorV3Interface.Contract.GetRoundData(&_AggregatorV3Interface.CallOpts, _roundId) -} - -// GetRoundData is a free data retrieval call binding the contract method 0x9a6fc8f5. -// -// Solidity: function getRoundData(uint80 _roundId) view returns(uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) -func (_AggregatorV3Interface *AggregatorV3InterfaceCallerSession) GetRoundData(_roundId *big.Int) (struct { - RoundId *big.Int - Answer *big.Int - StartedAt *big.Int - UpdatedAt *big.Int - AnsweredInRound *big.Int -}, error) { - return _AggregatorV3Interface.Contract.GetRoundData(&_AggregatorV3Interface.CallOpts, _roundId) -} - -// LatestRoundData is a free data retrieval call binding the contract method 0xfeaf968c. -// -// Solidity: function latestRoundData() view returns(uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) -func (_AggregatorV3Interface *AggregatorV3InterfaceCaller) LatestRoundData(opts *bind.CallOpts) (struct { - RoundId *big.Int - Answer *big.Int - StartedAt *big.Int - UpdatedAt *big.Int - AnsweredInRound *big.Int -}, error) { - var out []interface{} - err := _AggregatorV3Interface.contract.Call(opts, &out, "latestRoundData") - - outstruct := new(struct { - RoundId *big.Int - Answer *big.Int - StartedAt *big.Int - UpdatedAt *big.Int - AnsweredInRound *big.Int - }) - if err != nil { - return *outstruct, err - } - - outstruct.RoundId = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) - outstruct.Answer = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int) - outstruct.StartedAt = *abi.ConvertType(out[2], new(*big.Int)).(**big.Int) - outstruct.UpdatedAt = *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) - outstruct.AnsweredInRound = *abi.ConvertType(out[4], new(*big.Int)).(**big.Int) - - return *outstruct, err - -} - -// LatestRoundData is a free data retrieval call binding the contract method 0xfeaf968c. -// -// Solidity: function latestRoundData() view returns(uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) -func (_AggregatorV3Interface *AggregatorV3InterfaceSession) LatestRoundData() (struct { - RoundId *big.Int - Answer *big.Int - StartedAt *big.Int - UpdatedAt *big.Int - AnsweredInRound *big.Int -}, error) { - return _AggregatorV3Interface.Contract.LatestRoundData(&_AggregatorV3Interface.CallOpts) -} - -// LatestRoundData is a free data retrieval call binding the contract method 0xfeaf968c. -// -// Solidity: function latestRoundData() view returns(uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) -func (_AggregatorV3Interface *AggregatorV3InterfaceCallerSession) LatestRoundData() (struct { - RoundId *big.Int - Answer *big.Int - StartedAt *big.Int - UpdatedAt *big.Int - AnsweredInRound *big.Int -}, error) { - return _AggregatorV3Interface.Contract.LatestRoundData(&_AggregatorV3Interface.CallOpts) -} - -// Version is a free data retrieval call binding the contract method 0x54fd4d50. -// -// Solidity: function version() view returns(uint256) -func (_AggregatorV3Interface *AggregatorV3InterfaceCaller) Version(opts *bind.CallOpts) (*big.Int, error) { - var out []interface{} - err := _AggregatorV3Interface.contract.Call(opts, &out, "version") - - if err != nil { - return *new(*big.Int), err - } - - out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) - - return out0, err - -} - -// Version is a free data retrieval call binding the contract method 0x54fd4d50. -// -// Solidity: function version() view returns(uint256) -func (_AggregatorV3Interface *AggregatorV3InterfaceSession) Version() (*big.Int, error) { - return _AggregatorV3Interface.Contract.Version(&_AggregatorV3Interface.CallOpts) -} - -// Version is a free data retrieval call binding the contract method 0x54fd4d50. -// -// Solidity: function version() view returns(uint256) -func (_AggregatorV3Interface *AggregatorV3InterfaceCallerSession) Version() (*big.Int, error) { - return _AggregatorV3Interface.Contract.Version(&_AggregatorV3Interface.CallOpts) -} diff --git a/eth/contracts/chainlink/AggregatorV3Interface.sol b/eth/contracts/chainlink/AggregatorV3Interface.sol deleted file mode 100644 index 1bedfce21..000000000 --- a/eth/contracts/chainlink/AggregatorV3Interface.sol +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: MIT -// https://github.com/smartcontractkit/chainlink/blob/v2.9.1/contracts/src/v0.7/interfaces/AggregatorV3Interface.sol -pragma solidity ^0.7.0; - -interface AggregatorV3Interface { - function decimals() external view returns (uint8); - - function description() external view returns (string memory); - - function version() external view returns (uint256); - - // getRoundData and latestRoundData should both raise "No data present" - // if they do not have data to report, instead of returning unset values - // which could be misinterpreted as actual reported values. - function getRoundData(uint80 _roundId) - external - view - returns ( - uint80 roundId, - int256 answer, - uint256 startedAt, - uint256 updatedAt, - uint80 answeredInRound - ); - - function latestRoundData() - external - view - returns ( - uint80 roundId, - int256 answer, - uint256 startedAt, - uint256 updatedAt, - uint80 answeredInRound - ); -} diff --git a/eth/pricefeed.go b/eth/pricefeed.go deleted file mode 100644 index a9f51d012..000000000 --- a/eth/pricefeed.go +++ /dev/null @@ -1,78 +0,0 @@ -package eth - -import ( - "errors" - "fmt" - "math/big" - "time" - - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/ethclient" - "github.com/livepeer/go-livepeer/eth/contracts/chainlink" -) - -type PriceData struct { - RoundID int64 - Price *big.Rat - UpdatedAt time.Time -} - -// PriceFeedEthClient is an interface for fetching price data from a Chainlink -// PriceFeed contract. -type PriceFeedEthClient interface { - Description() (string, error) - FetchPriceData() (PriceData, error) -} - -func NewPriceFeedEthClient(ethClient *ethclient.Client, priceFeedAddr string) (PriceFeedEthClient, error) { - addr := common.HexToAddress(priceFeedAddr) - priceFeed, err := chainlink.NewAggregatorV3Interface(addr, ethClient) - if err != nil { - return nil, fmt.Errorf("failed to create aggregator proxy: %w", err) - } - - return &priceFeedClient{ - client: ethClient, - priceFeed: priceFeed, - }, nil -} - -type priceFeedClient struct { - client *ethclient.Client - priceFeed *chainlink.AggregatorV3Interface -} - -func (c *priceFeedClient) Description() (string, error) { - return c.priceFeed.Description(&bind.CallOpts{}) -} - -func (c *priceFeedClient) FetchPriceData() (PriceData, error) { - data, err := c.priceFeed.LatestRoundData(&bind.CallOpts{}) - if err != nil { - return PriceData{}, errors.New("failed to get latest round data: " + err.Error()) - } - - decimals, err := c.priceFeed.Decimals(&bind.CallOpts{}) - if err != nil { - return PriceData{}, errors.New("failed to get decimals: " + err.Error()) - } - - return computePriceData(data.RoundId, data.UpdatedAt, data.Answer, decimals), nil -} - -// computePriceData transforms the raw data from the PriceFeed into the higher -// level PriceData struct, more easily usable by the rest of the system. -func computePriceData(roundID, updatedAt, answer *big.Int, decimals uint8) PriceData { - // Compute a big.int which is 10^decimals. - divisor := new(big.Int).Exp( - big.NewInt(10), - big.NewInt(int64(decimals)), - nil) - - return PriceData{ - RoundID: roundID.Int64(), - Price: new(big.Rat).SetFrac(answer, divisor), - UpdatedAt: time.Unix(updatedAt.Int64(), 0), - } -} diff --git a/eth/pricefeed_test.go b/eth/pricefeed_test.go deleted file mode 100644 index 981a158e7..000000000 --- a/eth/pricefeed_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package eth - -import ( - "math/big" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestComputePriceData(t *testing.T) { - assert := assert.New(t) - - t.Run("valid data", func(t *testing.T) { - roundID := big.NewInt(1) - updatedAt := big.NewInt(1626192000) - answer := big.NewInt(420666000) - decimals := uint8(6) - - data := computePriceData(roundID, updatedAt, answer, decimals) - - assert.EqualValues(int64(1), data.RoundID, "Round ID didn't match") - assert.Equal("210333/500", data.Price.RatString(), "The Price Rat didn't match") - assert.Equal("2021-07-13 16:00:00 +0000 UTC", data.UpdatedAt.UTC().String(), "The updated at time did not match") - }) - - t.Run("zero answer", func(t *testing.T) { - roundID := big.NewInt(2) - updatedAt := big.NewInt(1626192000) - answer := big.NewInt(0) - decimals := uint8(18) - - data := computePriceData(roundID, updatedAt, answer, decimals) - - assert.EqualValues(int64(2), data.RoundID, "Round ID didn't match") - assert.Equal("0", data.Price.RatString(), "The Price Rat didn't match") - assert.Equal("2021-07-13 16:00:00 +0000 UTC", data.UpdatedAt.UTC().String(), "The updated at time did not match") - }) - - t.Run("zero decimals", func(t *testing.T) { - roundID := big.NewInt(3) - updatedAt := big.NewInt(1626192000) - answer := big.NewInt(13) - decimals := uint8(0) - - data := computePriceData(roundID, updatedAt, answer, decimals) - - assert.EqualValues(int64(3), data.RoundID, "Round ID didn't match") - assert.Equal("13", data.Price.RatString(), "The Price Rat didn't match") - assert.Equal("2021-07-13 16:00:00 +0000 UTC", data.UpdatedAt.UTC().String(), "The updated at time did not match") - }) -} diff --git a/eth/roundinitializer.go b/eth/roundinitializer.go index a7aaff666..d3f2cbeee 100644 --- a/eth/roundinitializer.go +++ b/eth/roundinitializer.go @@ -2,11 +2,10 @@ package eth import ( "math/big" - "math/rand" "sync" - "time" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/event" "github.com/golang/glog" ) @@ -30,22 +29,20 @@ type timeWatcher interface { // This selection process is purely a client side implementation that attempts to minimize on-chain transaction collisions, but // collisions are still possible if initialization transactions are submitted by parties that are not using this selection process type RoundInitializer struct { - maxDelay time.Duration - client LivepeerEthClient - tw timeWatcher - quit chan struct{} + client LivepeerEthClient + tw timeWatcher + quit chan struct{} nextRoundStartL1Block *big.Int mu sync.Mutex } // NewRoundInitializer creates a RoundInitializer instance -func NewRoundInitializer(client LivepeerEthClient, tw timeWatcher, maxDelay time.Duration) *RoundInitializer { +func NewRoundInitializer(client LivepeerEthClient, tw timeWatcher) *RoundInitializer { return &RoundInitializer{ - maxDelay: maxDelay, - client: client, - tw: tw, - quit: make(chan struct{}), + client: client, + tw: tw, + quit: make(chan struct{}), } } @@ -107,23 +104,23 @@ func (r *RoundInitializer) tryInitialize() error { r.mu.Lock() defer r.mu.Unlock() - if r.tw.LastSeenL1Block().Cmp(r.nextRoundStartL1Block) < 0 { - // Round already initialized - return nil - } + currentL1Blk := r.tw.LastSeenL1Block() + lastInitializedL1BlkHash := r.tw.LastInitializedL1BlockHash() - if r.maxDelay > 0 { - randDelay := time.Duration(rand.Int63n(int64(r.maxDelay))) - glog.Infof("Waiting %v before attempting to initialize round", randDelay) - time.Sleep(randDelay) + epochSeed := r.currentEpochSeed(currentL1Blk, r.nextRoundStartL1Block, lastInitializedL1BlkHash) - if r.tw.LastSeenL1Block().Cmp(r.nextRoundStartL1Block) < 0 { - glog.Infof("Round is already initialized, not initializing") - return nil - } + ok, err := r.shouldInitialize(epochSeed) + if err != nil { + return err + } + + // Noop if the caller should not initialize the round + if !ok { + return nil } currentRound := new(big.Int).Add(r.tw.LastInitializedRound(), big.NewInt(1)) + glog.Infof("New round - preparing to initialize round to join active set, current round is %d", currentRound) tx, err := r.client.InitializeRound() @@ -139,3 +136,55 @@ func (r *RoundInitializer) tryInitialize() error { return nil } + +func (r *RoundInitializer) shouldInitialize(epochSeed *big.Int) (bool, error) { + transcoders, err := r.client.TranscoderPool() + if err != nil { + return false, err + } + + numActive := big.NewInt(int64(len(transcoders))) + + // Should not initialize if the upcoming active set is empty + if numActive.Cmp(big.NewInt(0)) == 0 { + return false, nil + } + + // Find the caller's rank in the upcoming active set + rank := int64(-1) + maxRank := numActive.Int64() + caller := r.client.Account().Address + for i := int64(0); i < maxRank; i++ { + if transcoders[i].Address == caller { + rank = i + break + } + } + + // Should not initialize if the caller is not in the upcoming active set + if rank == -1 { + return false, nil + } + + // Use the seed to select a position within the active set + selection := new(big.Int).Mod(epochSeed, numActive) + // Should not initialize if the selection does not match the caller's rank in the active set + if selection.Int64() != int64(rank) { + return false, nil + } + + // If the selection matches the caller's rank the caller should initialize the round + return true, nil +} + +// Returns the seed used to select a round initializer in the current epoch for the current round +// This seed is not meant to be unpredictable. The only requirement for the seed is that it is calculated the same way for each +// party running the round initializer +func (r *RoundInitializer) currentEpochSeed(currentL1Block, roundStartL1Block *big.Int, lastInitializedL1BlkHash [32]byte) *big.Int { + epochNum := new(big.Int).Sub(currentL1Block, roundStartL1Block) + epochNum.Div(epochNum, epochL1Blocks) + + // The seed for the current epoch is calculated as: + // keccak256(lastInitializedL1BlkHash | epochNum) + return crypto.Keccak256Hash(append(lastInitializedL1BlkHash[:], epochNum.Bytes()...)).Big() +} diff --git a/eth/roundinitializer_test.go b/eth/roundinitializer_test.go index a96cb3b2f..25e60205a 100644 --- a/eth/roundinitializer_test.go +++ b/eth/roundinitializer_test.go @@ -16,6 +16,84 @@ import ( "github.com/stretchr/testify/mock" ) +func TestRoundInitializer_CurrentEpochSeed(t *testing.T) { + initializer := NewRoundInitializer(nil, nil) + + assert := assert.New(t) + + // Test epochNum = 0 + blkHash := [32]byte{123} + + epochSeed := initializer.currentEpochSeed(big.NewInt(5), big.NewInt(5), blkHash) + // epochNum = (5 - 5) / 5 = 0 + // epochSeed = keccak256(blkHash | 0) = 53205358842179480591542570540016728811976439286094436690881169143335261643310 + expEpochSeed, _ := new(big.Int).SetString("53205358842179480591542570540016728811976439286094436690881169143335261643310", 10) + assert.Equal(expEpochSeed, epochSeed) + + // Test epochNum > 0 + epochSeed = initializer.currentEpochSeed(big.NewInt(20), big.NewInt(5), blkHash) + // epochNum = (20 - 5) / 5 = 3 + // epochSeed = keccak256(blkHash | 3) = 42541119854153860846042329644941941146216657514071318786342840580076059276721 + expEpochSeed.SetString("42541119854153860846042329644941941146216657514071318786342840580076059276721", 10) + assert.Equal(expEpochSeed, epochSeed) + + // Test epochNum > 0 with some # of blocks into the epoch + epochSeed = initializer.currentEpochSeed(big.NewInt(20), big.NewInt(4), blkHash) + // epochNum = (20 - 4) / 5 = 3.2 -> 3 + assert.Equal(expEpochSeed, epochSeed) +} + +func TestRoundInitializer_ShouldInitialize(t *testing.T) { + client := &MockClient{} + tw := &stubTimeWatcher{} + initializer := NewRoundInitializer(client, tw) + + assert := assert.New(t) + + // Test error getting transcoders + expErr := errors.New("TranscoderPool error") + client.On("TranscoderPool").Return(nil, expErr).Once() + + ok, err := initializer.shouldInitialize(nil) + assert.EqualError(err, expErr.Error()) + assert.False(ok) + + // Test active set is empty because no registered transcoders + client.On("TranscoderPool").Return([]*lpTypes.Transcoder{}, nil).Once() + ok, err = initializer.shouldInitialize(nil) + assert.Nil(err) + assert.False(ok) + + // Test that caller is not in active set because it is not registered + caller := ethcommon.BytesToAddress([]byte("foo")) + client.On("Account").Return(accounts.Account{Address: caller}) + + registered := []*lpTypes.Transcoder{ + {Address: ethcommon.BytesToAddress([]byte("jar"))}, + {Address: ethcommon.BytesToAddress([]byte("bar"))}, + } + client.On("TranscoderPool").Return(registered, nil).Once() + + ok, err = initializer.shouldInitialize(nil) + assert.Nil(err) + assert.False(ok) + + // Test not selected + registered = append(registered, &lpTypes.Transcoder{Address: caller}) + client.On("TranscoderPool").Return(registered, nil) + + seed := big.NewInt(3) + ok, err = initializer.shouldInitialize(seed) + assert.Nil(err) + assert.False(ok) + + // Test caller selected + seed = big.NewInt(5) + ok, err = initializer.shouldInitialize(seed) + assert.Nil(err) + assert.True(ok) +} + func TestRoundInitializer_TryInitialize(t *testing.T) { client := &MockClient{} tw := &stubTimeWatcher{ @@ -23,17 +101,45 @@ func TestRoundInitializer_TryInitialize(t *testing.T) { lastInitializedRound: big.NewInt(100), lastInitializedBlockHash: [32]byte{123}, } - initializer := NewRoundInitializer(client, tw, 0) + initializer := NewRoundInitializer(client, tw) initializer.nextRoundStartL1Block = big.NewInt(5) assert := assert.New(t) + // Test error checking should initialize + expErr := errors.New("shouldInitialize error") + client.On("TranscoderPool").Return(nil, expErr).Once() + + err := initializer.tryInitialize() + assert.EqualError(err, expErr.Error()) + + // Test should not initialize + caller := ethcommon.BytesToAddress([]byte("foo")) + client.On("Account").Return(accounts.Account{Address: caller}) + + registered := []*lpTypes.Transcoder{ + {Address: ethcommon.BytesToAddress([]byte("jar"))}, + } + client.On("TranscoderPool").Return(registered, nil).Once() + + err = initializer.tryInitialize() + assert.Nil(err) + + // Test error when submitting initialization tx + registered = []*lpTypes.Transcoder{{Address: caller}} + client.On("TranscoderPool").Return(registered, nil) + expErr = errors.New("InitializeRound error") + client.On("InitializeRound").Return(nil, expErr).Once() + + err = initializer.tryInitialize() + assert.EqualError(err, expErr.Error()) + // Test error checking initialization tx tx := &types.Transaction{} client.On("InitializeRound").Return(tx, nil) - expErr := errors.New("CheckTx error") + expErr = errors.New("CheckTx error") client.On("CheckTx", mock.Anything).Return(expErr).Once() - err := initializer.tryInitialize() + err = initializer.tryInitialize() assert.EqualError(err, expErr.Error()) // Test success diff --git a/eth/watchers/pricefeedwatcher.go b/eth/watchers/pricefeedwatcher.go deleted file mode 100644 index 1bab9dc13..000000000 --- a/eth/watchers/pricefeedwatcher.go +++ /dev/null @@ -1,234 +0,0 @@ -package watchers - -import ( - "context" - "fmt" - "strings" - "sync" - "time" - - "github.com/ethereum/go-ethereum/ethclient" - "github.com/ethereum/go-ethereum/event" - "github.com/livepeer/go-livepeer/clog" - "github.com/livepeer/go-livepeer/eth" -) - -const ( - priceUpdateMaxRetries = 5 - priceUpdateBaseRetryDelay = 30 * time.Second - priceUpdatePeriod = 1 * time.Hour -) - -type PriceFeedWatcher interface { - Currencies() (base string, quote string, err error) - Current() (eth.PriceData, error) - Subscribe(ctx context.Context, sink chan<- eth.PriceData) -} - -// PriceFeedWatcher monitors a Chainlink PriceFeed for updated pricing info. It -// allows fetching the current price as well as listening for updates on the -// PriceUpdated channel. -type priceFeedWatcher struct { - baseRetryDelay time.Duration - - priceFeed eth.PriceFeedEthClient - - mu sync.RWMutex - current eth.PriceData - cancelWatch func() - - priceEventFeed event.Feed - subscriptions event.SubscriptionScope -} - -// NewPriceFeedWatcher creates a new PriceFeedWatcher instance. It will already -// fetch the current price and start a goroutine to watch for updates. -func NewPriceFeedWatcher(ethClient *ethclient.Client, priceFeedAddr string) (PriceFeedWatcher, error) { - priceFeed, err := eth.NewPriceFeedEthClient(ethClient, priceFeedAddr) - if err != nil { - return nil, fmt.Errorf("failed to create price feed client: %w", err) - } - w := &priceFeedWatcher{ - baseRetryDelay: priceUpdateBaseRetryDelay, - priceFeed: priceFeed, - } - return w, nil -} - -// Currencies returns the base and quote currencies of the price feed. -// i.e. base = CurrentPrice() * quote -func (w *priceFeedWatcher) Currencies() (base string, quote string, err error) { - description, err := w.priceFeed.Description() - if err != nil { - return "", "", fmt.Errorf("failed to get description: %w", err) - } - - base, quote, err = parseCurrencies(description) - if err != nil { - return "", "", err - } - return -} - -// Current returns the latest fetched price data, or fetches it in case it has -// not been fetched yet. -func (w *priceFeedWatcher) Current() (eth.PriceData, error) { - w.mu.RLock() - current := w.current - w.mu.RUnlock() - if current.UpdatedAt.IsZero() { - return w.updatePrice() - } - return current, nil -} - -// Subscribe allows one to subscribe to price updates emitted by the Watcher. -// The sink channel should have ample buffer space to avoid blocking other -// subscribers. Slow subscribers are not dropped. The subscription is kept alive -// until the passed Context is cancelled. -// -// The watch loop is run automatically while there are active subscriptions. It -// will be started when the first subscription is made and is automatically -// stopped when the last subscription is closed. -func (w *priceFeedWatcher) Subscribe(ctx context.Context, sink chan<- eth.PriceData) { - w.mu.Lock() - defer w.mu.Unlock() - w.ensureWatchLocked() - - sub := w.subscriptions.Track(w.priceEventFeed.Subscribe(sink)) - go w.handleUnsubscribe(ctx, sub) -} - -// updatePrice fetches the latest price data from the price feed and updates the -// current price if it is newer. If the price is updated, it will also send the -// updated price to the price event feed. -func (w *priceFeedWatcher) updatePrice() (eth.PriceData, error) { - newPrice, err := w.priceFeed.FetchPriceData() - if err != nil { - return eth.PriceData{}, fmt.Errorf("failed to fetch price data: %w", err) - } - - if newPrice.UpdatedAt.After(w.current.UpdatedAt) { - w.mu.Lock() - w.current = newPrice - w.mu.Unlock() - w.priceEventFeed.Send(newPrice) - } - - return newPrice, nil -} - -// ensureWatchLocked makes sure that the watch process is running. It assumes it -// is already running in a locked context (w.mu). The watch process itself will -// run in background and periodically poll the price feed for updates until the -// `w.cancelWatch` function is called. -func (w *priceFeedWatcher) ensureWatchLocked() { - if w.cancelWatch != nil { - // already running - return - } - ctx, cancel := context.WithCancel(context.Background()) - w.cancelWatch = cancel - - ticker := newTruncatedTicker(ctx, priceUpdatePeriod) - go w.watchTicker(ctx, ticker) -} - -// handleUnsubscribe waits for the provided Context to be done and then closes -// the given subscription. It then stops the watch process if there are no more -// active subscriptions. -func (w *priceFeedWatcher) handleUnsubscribe(ctx context.Context, sub event.Subscription) { -loop: - for { - select { - case <-ctx.Done(): - break loop - case <-sub.Err(): - clog.Errorf(ctx, "PriceFeedWatcher subscription error: %v", sub.Err()) - } - } - w.mu.Lock() - defer w.mu.Unlock() - - sub.Unsubscribe() - if w.subscriptions.Count() == 0 && w.cancelWatch != nil { - w.cancelWatch() - w.cancelWatch = nil - } -} - -// watchTicker is the main loop that periodically fetches the latest price data -// from the price feed. It's lifecycle is handled through the ensureWatch and -// handleUnsubscribe functions. -func (w *priceFeedWatcher) watchTicker(ctx context.Context, ticker <-chan time.Time) { - clog.V(6).Infof(ctx, "Starting PriceFeed watch loop") - for { - select { - case <-ctx.Done(): - clog.V(6).Infof(ctx, "Stopping PriceFeed watch loop") - return - case <-ticker: - attempt, retryDelay := 1, w.baseRetryDelay - for { - _, err := w.updatePrice() - if err == nil { - break - } else if attempt >= priceUpdateMaxRetries { - clog.Errorf(ctx, "Failed to fetch updated price from PriceFeed attempts=%d err=%q", attempt, err) - break - } - - clog.Warningf(ctx, "Failed to fetch updated price from PriceFeed, retrying after retryDelay=%d attempt=%d err=%q", retryDelay, attempt, err) - select { - case <-ctx.Done(): - return - case <-time.After(retryDelay): - } - attempt, retryDelay = attempt+1, retryDelay*2 - } - } - } -} - -// parseCurrencies parses the base and quote currencies from a price feed based -// on Chainlink PriceFeed description pattern "FROM / TO". -func parseCurrencies(description string) (currencyBase string, currencyQuote string, err error) { - currencies := strings.Split(description, "/") - if len(currencies) != 2 { - return "", "", fmt.Errorf("aggregator description must be in the format 'FROM / TO' but got: %s", description) - } - - currencyBase = strings.TrimSpace(currencies[0]) - currencyQuote = strings.TrimSpace(currencies[1]) - return -} - -// newTruncatedTicker creates a ticker that ticks at the next time that is a -// multiple of d, starting from the current time. This is a best-effort approach -// to ensure that nodes update their prices around the same time to avoid too -// big price discrepancies. -func newTruncatedTicker(ctx context.Context, d time.Duration) <-chan time.Time { - ch := make(chan time.Time, 1) - go func() { - // Do not close the channel, to prevent a concurrent goroutine reading from - // the channel from seeing an erroneous "tick" after its closed. - - nextTick := time.Now().UTC().Truncate(d) - for { - nextTick = nextTick.Add(d) - untilNextTick := nextTick.Sub(time.Now().UTC()) - if untilNextTick <= 0 { - continue - } - - select { - case <-ctx.Done(): - return - case t := <-time.After(untilNextTick): - ch <- t - } - } - }() - - return ch -} diff --git a/eth/watchers/pricefeedwatcher_test.go b/eth/watchers/pricefeedwatcher_test.go deleted file mode 100644 index 27eb5d24d..000000000 --- a/eth/watchers/pricefeedwatcher_test.go +++ /dev/null @@ -1,249 +0,0 @@ -package watchers - -import ( - "context" - "errors" - "math/big" - "reflect" - "testing" - "time" - - "github.com/livepeer/go-livepeer/eth" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" -) - -type mockPriceFeedEthClient struct { - mock.Mock -} - -func (m *mockPriceFeedEthClient) FetchPriceData() (eth.PriceData, error) { - args := m.Called() - return args.Get(0).(eth.PriceData), args.Error(1) -} - -func (m *mockPriceFeedEthClient) Description() (string, error) { - args := m.Called() - return args.String(0), args.Error(1) -} - -func TestPriceFeedWatcher_UpdatePrice(t *testing.T) { - priceFeedMock := new(mockPriceFeedEthClient) - defer priceFeedMock.AssertExpectations(t) - - priceData := eth.PriceData{ - RoundID: 10, - Price: big.NewRat(3, 2), - UpdatedAt: time.Now(), - } - priceFeedMock.On("FetchPriceData").Return(priceData, nil).Once() - - w := &priceFeedWatcher{priceFeed: priceFeedMock} - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - priceUpdated := make(chan eth.PriceData, 1) - w.Subscribe(ctx, priceUpdated) - - newPrice, err := w.updatePrice() - require.NoError(t, err) - require.Equal(t, priceData, newPrice) - - select { - case updatedPrice := <-priceUpdated: - require.Equal(t, priceData, updatedPrice) - case <-time.After(2 * time.Second): - t.Error("Updated price hasn't been received on channel") - } -} - -func TestPriceFeedWatcher_Subscribe(t *testing.T) { - require := require.New(t) - priceFeedMock := new(mockPriceFeedEthClient) - defer priceFeedMock.AssertExpectations(t) - - w := &priceFeedWatcher{priceFeed: priceFeedMock} - - // Start a bunch of subscriptions and make sure only 1 watch loop gets started - observedCancelWatch := []context.CancelFunc{} - cancelSub := []context.CancelFunc{} - for i := 0; i < 5; i++ { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - w.Subscribe(ctx, make(chan eth.PriceData, 1)) - - observedCancelWatch = append(observedCancelWatch, w.cancelWatch) - cancelSub = append(cancelSub, cancel) - } - - require.NotNil(w.cancelWatch) - for i := range observedCancelWatch { - require.Equal(reflect.ValueOf(w.cancelWatch).Pointer(), reflect.ValueOf(observedCancelWatch[i]).Pointer()) - } - - // Stop all but the last subscription and ensure watch loop stays running - for i := 0; i < 4; i++ { - cancelSub[i]() - require.NotNil(w.cancelWatch) - } - - // Now stop the last subscription and ensure watch loop gets stopped - cancelSub[4]() - time.Sleep(1 * time.Second) - require.Nil(w.cancelWatch) - - // Finally, just make sure it can be started again after having been stopped - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - w.Subscribe(ctx, make(chan eth.PriceData, 1)) - require.NotNil(w.cancelWatch) -} - -func TestPriceFeedWatcher_Watch(t *testing.T) { - require := require.New(t) - priceFeedMock := new(mockPriceFeedEthClient) - defer priceFeedMock.AssertExpectations(t) - - w := &priceFeedWatcher{priceFeed: priceFeedMock} - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - priceUpdated := make(chan eth.PriceData, 1) - w.Subscribe(ctx, priceUpdated) - - priceData := eth.PriceData{ - RoundID: 10, - Price: big.NewRat(9, 2), - UpdatedAt: time.Now(), - } - checkPriceUpdated := func() { - select { - case updatedPrice := <-priceUpdated: - require.Equal(priceData, updatedPrice) - require.Equal(priceData, w.current) - case <-time.After(1 * time.Second): - require.Fail("Updated price hasn't been received on channel in a timely manner") - } - priceFeedMock.AssertExpectations(t) - } - checkNoPriceUpdate := func() { - select { - case <-priceUpdated: - require.Fail("Unexpected price update given it hasn't changed") - case <-time.After(1 * time.Second): - // all good - } - priceFeedMock.AssertExpectations(t) - } - - // Start the watch loop - fakeTicker := make(chan time.Time, 10) - go func() { - w.watchTicker(ctx, fakeTicker) - }() - - // First time should trigger an update - priceFeedMock.On("FetchPriceData").Return(priceData, nil).Once() - fakeTicker <- time.Now() - checkPriceUpdated() - - // Trigger a dummy update given price hasn't changed - priceFeedMock.On("FetchPriceData").Return(priceData, nil).Once() - fakeTicker <- time.Now() - checkNoPriceUpdate() - - // still shouldn't update given UpdatedAt stayed the same - priceData.Price = big.NewRat(1, 1) - priceFeedMock.On("FetchPriceData").Return(priceData, nil).Once() - fakeTicker <- time.Now() - checkNoPriceUpdate() - - // bump the UpdatedAt time to trigger an update - priceData.UpdatedAt = priceData.UpdatedAt.Add(1 * time.Minute) - priceFeedMock.On("FetchPriceData").Return(priceData, nil).Once() - fakeTicker <- time.Now() - checkPriceUpdated() - - priceData.UpdatedAt = priceData.UpdatedAt.Add(1 * time.Hour) - priceData.Price = big.NewRat(3, 2) - priceFeedMock.On("FetchPriceData").Return(priceData, nil).Once() - fakeTicker <- time.Now() - checkPriceUpdated() -} - -func TestPriceFeedWatcher_WatchErrorRetries(t *testing.T) { - priceFeedMock := new(mockPriceFeedEthClient) - defer priceFeedMock.AssertExpectations(t) - - // First 4 calls should fail then succeed on the 5th - for i := 0; i < 4; i++ { - priceFeedMock.On("FetchPriceData").Return(eth.PriceData{}, errors.New("error")).Once() - } - priceData := eth.PriceData{ - RoundID: 10, - Price: big.NewRat(3, 2), - UpdatedAt: time.Now(), - } - priceFeedMock.On("FetchPriceData").Return(priceData, nil) - - w := &priceFeedWatcher{ - baseRetryDelay: 5 * time.Millisecond, - priceFeed: priceFeedMock, - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - priceUpdated := make(chan eth.PriceData, 1) - w.Subscribe(ctx, priceUpdated) - - // Start watch loop - fakeTicker := make(chan time.Time, 10) - go func() { - w.watchTicker(ctx, fakeTicker) - }() - - fakeTicker <- time.Now() - select { - case updatedPrice := <-priceUpdated: - require.Equal(t, priceData, updatedPrice) - case <-time.After(2 * time.Second): - t.Error("Updated price hasn't been received on channel") - } -} - -func TestParseCurrencies(t *testing.T) { - t.Run("Valid currencies", func(t *testing.T) { - description := "ETH / USD" - currencyBase, currencyQuote, err := parseCurrencies(description) - - require.NoError(t, err) - require.Equal(t, "ETH", currencyBase) - require.Equal(t, "USD", currencyQuote) - }) - - t.Run("Missing separator", func(t *testing.T) { - description := "ETHUSD" - _, _, err := parseCurrencies(description) - - require.Error(t, err) - require.Contains(t, err.Error(), "aggregator description must be in the format 'FROM / TO'") - }) - - t.Run("Extra spaces", func(t *testing.T) { - description := " ETH / USD " - currencyBase, currencyQuote, err := parseCurrencies(description) - - require.NoError(t, err) - require.Equal(t, "ETH", currencyBase) - require.Equal(t, "USD", currencyQuote) - }) - - t.Run("Lowercase currency", func(t *testing.T) { - description := "eth / usd" - currencyBase, currencyQuote, err := parseCurrencies(description) - - require.NoError(t, err) - require.Equal(t, "eth", currencyBase) - require.Equal(t, "usd", currencyQuote) - }) -} diff --git a/go.mod b/go.mod index 1a59fc36f..c80fe64f3 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,10 @@ go 1.21.5 require ( contrib.go.opencensus.io/exporter/prometheus v0.4.2 - github.com/Masterminds/semver/v3 v3.2.1 github.com/cenkalti/backoff v2.2.1+incompatible github.com/ethereum/go-ethereum v1.13.5 github.com/getkin/kin-openapi v0.124.0 - github.com/golang/glog v1.2.1 + github.com/golang/glog v1.1.1 github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.4 github.com/jaypipes/ghw v0.10.0 @@ -16,7 +15,7 @@ require ( github.com/livepeer/ai-worker v0.1.0 github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 - github.com/livepeer/lpms v0.0.0-20240731140137-28406cf8bc78 + github.com/livepeer/lpms v0.0.0-20240711175220-227325841434 github.com/livepeer/m3u8 v0.11.1 github.com/mattn/go-sqlite3 v1.14.18 github.com/oapi-codegen/nethttp-middleware v1.0.1 @@ -32,34 +31,27 @@ require ( go.opencensus.io v0.24.0 go.uber.org/goleak v1.3.0 golang.org/x/net v0.25.0 - google.golang.org/grpc v1.65.0 - google.golang.org/protobuf v1.34.1 + google.golang.org/grpc v1.57.1 + google.golang.org/protobuf v1.33.0 pgregory.net/rapid v1.1.0 ) require ( cloud.google.com/go v0.110.2 // indirect - cloud.google.com/go/compute/metadata v0.3.0 // indirect + cloud.google.com/go/compute v1.20.0 // indirect + cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v1.1.0 // indirect cloud.google.com/go/storage v1.30.1 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/DataDog/zstd v1.4.5 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/StackExchange/wmi v1.2.1 // indirect - github.com/VictoriaMetrics/fastcache v1.12.1 // indirect github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/aws/aws-sdk-go v1.44.273 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.7.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect github.com/cespare/cp v1.1.1 // indirect - github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/cockroachdb/errors v1.8.1 // indirect - github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f // indirect - github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 // indirect - github.com/cockroachdb/redact v1.0.8 // indirect - github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 // indirect - github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/consensys/bavard v0.1.13 // indirect github.com/consensys/gnark-crypto v0.12.1 // indirect github.com/containerd/containerd v1.7.0-beta.2 // indirect @@ -68,7 +60,6 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/deckarep/golang-set/v2 v2.1.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect - github.com/deepmap/oapi-codegen v1.6.0 // indirect github.com/deepmap/oapi-codegen/v2 v2.2.0 // indirect github.com/distribution/reference v0.5.0 // indirect github.com/dlclark/regexp2 v1.7.0 // indirect @@ -81,7 +72,6 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/ethereum/c-kzg-4844 v0.4.0 // indirect github.com/fatih/color v1.13.0 // indirect - github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect github.com/ghodss/yaml v1.0.0 // indirect @@ -96,28 +86,19 @@ require ( github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/go-test/deep v1.1.0 // indirect - github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect github.com/google/s2a-go v0.1.4 // indirect - github.com/google/uuid v1.6.0 // indirect + github.com/google/uuid v1.5.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect github.com/googleapis/gax-go/v2 v2.10.0 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/websocket v1.4.2 // indirect - github.com/graph-gophers/graphql-go v1.3.0 // indirect - github.com/hashicorp/go-bexpr v0.1.10 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 // indirect - github.com/holiman/bloomfilter/v2 v2.0.3 // indirect github.com/holiman/uint256 v1.2.3 // indirect - github.com/huin/goupnp v1.3.0 // indirect - github.com/influxdata/influxdb-client-go/v2 v2.4.0 // indirect - github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c // indirect - github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect github.com/invopop/yaml v0.2.0 // indirect github.com/ipfs/bbloom v0.0.4 // indirect github.com/ipfs/go-block-format v0.1.2 // indirect @@ -141,7 +122,6 @@ require ( github.com/ipld/go-car v0.6.0 // indirect github.com/ipld/go-codec-dagpb v1.6.0 // indirect github.com/ipld/go-ipld-prime v0.20.0 // indirect - github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/goprocess v0.1.4 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -149,8 +129,6 @@ require ( github.com/karalabe/usb v0.0.2 // indirect github.com/klauspost/compress v1.17.4 // indirect github.com/klauspost/cpuid/v2 v2.2.6 // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/kr/text v0.2.0 // indirect github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -161,8 +139,6 @@ require ( github.com/minio/minio-go/v7 v7.0.66 // indirect github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/mapstructure v1.4.1 // indirect - github.com/mitchellh/pointerstructure v1.2.0 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/sys/sequential v0.5.0 // indirect @@ -191,8 +167,6 @@ require ( github.com/rabbitmq/amqp091-go v1.8.0 // indirect github.com/rabbitmq/rabbitmq-stream-go-client v1.1.1 // indirect github.com/rivo/uniseg v0.2.0 // indirect - github.com/rogpeppe/go-internal v1.12.0 // indirect - github.com/rs/cors v1.7.0 // indirect github.com/rs/xid v1.5.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect @@ -201,14 +175,10 @@ require ( github.com/status-im/keycard-go v0.2.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/supranational/blst v0.3.11 // indirect - github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect - github.com/tyler-smith/go-bip39 v1.1.0 // indirect - github.com/urfave/cli/v2 v2.25.7 // indirect github.com/vincent-petithory/dataurl v1.0.0 // indirect github.com/whyrusleeping/cbor-gen v0.0.0-20230418232409-daab9ece03a0 // indirect - github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect go.opentelemetry.io/otel v1.16.0 // indirect go.opentelemetry.io/otel/metric v1.16.0 // indirect go.opentelemetry.io/otel/trace v1.16.0 // indirect @@ -218,20 +188,18 @@ require ( golang.org/x/crypto v0.23.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/oauth2 v0.20.0 // indirect + golang.org/x/oauth2 v0.8.0 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.20.0 // indirect golang.org/x/text v0.15.0 // indirect - golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.21.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.125.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect howett.net/plist v1.0.0 // indirect diff --git a/go.sum b/go.sum index 2e9eacc71..74df1b726 100644 --- a/go.sum +++ b/go.sum @@ -21,8 +21,10 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= -cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute v1.20.0 h1:cUOcywWuowO9It2i1KX1lIb0HH7gLv6nENKuZGnlcSo= +cloud.google.com/go/compute v1.20.0/go.mod h1:kn5BhC++qUWR/AM3Dn21myV7QbgqejW04cAOrtppaQI= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/iam v1.1.0 h1:67gSqaPukx7O8WLLHMa0PNs3EBGd2eE4d+psbO/CO94= @@ -43,23 +45,14 @@ contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20221206110420-d395f97c4830 h1:u8scGKApGy+gXpYDw2f+nh60R0FqCfrpDRIQki+5o3U= github.com/AdaLogics/go-fuzz-headers v0.0.0-20221206110420-d395f97c4830/go.mod h1:VzwV+t+dZ9j/H867F1M2ziD+yLHtB46oM35FxxMJ4d0= -github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw= -github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= -github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM= -github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= -github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= @@ -67,27 +60,21 @@ github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXn github.com/Microsoft/hcsshim v0.10.0-rc.1 h1:Lms8jwpaIdIUvoBNee8ZuvIi1XnNy9uvnxSC9L1q1x4= github.com/Microsoft/hcsshim v0.10.0-rc.1/go.mod h1:7XX96hdvnwWGdXnksDNdhfFcUH1BtQY6bL2L3f9Abyk= github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= -github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= -github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= -github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= -github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/aws/aws-sdk-go v1.44.273 h1:CX8O0gK+cGrgUyv7bgJ6QQP9mQg7u5mweHdNzULH47c= github.com/aws/aws-sdk-go v1.44.273/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= -github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -109,9 +96,8 @@ github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= -github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= @@ -127,10 +113,6 @@ github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XP github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4= -github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= -github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= -github.com/cockroachdb/errors v1.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM= github.com/cockroachdb/errors v1.8.1 h1:A5+txlVZfOqFBDa4mGz2bUWSp0aHElvHX2bKkdbQu+Y= github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac= github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY= @@ -143,7 +125,6 @@ github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 h1:IKgmqgMQlVJIZj19CdocBeS github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= -github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= @@ -153,11 +134,7 @@ github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMX github.com/containerd/containerd v1.7.0-beta.2 h1:GWmC96y8j7jlFJX0Wh+covft0M1hHBqQL7lo+N6qvxg= github.com/containerd/containerd v1.7.0-beta.2/go.mod h1:RR01Jsm/jovDKK48sFCVqWyKAH2APMPi88Aeu1on63I= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -166,7 +143,6 @@ github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXk github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0= github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis= -github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -178,13 +154,8 @@ github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= -github.com/deepmap/oapi-codegen v1.6.0 h1:w/d1ntwh91XI0b/8ja7+u5SvA4IFfM0UNNLmiDR1gg0= -github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= github.com/deepmap/oapi-codegen/v2 v2.2.0 h1:FW4f7C0Xb6EaezBSB3GYw2QGwHD5ChDflG+3xSZBdvY= github.com/deepmap/oapi-codegen/v2 v2.2.0/go.mod h1:L4zUv7ULYDtYSb/aYk/xO3OYcQU6BoU/0viULkbi2DE= -github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= @@ -209,53 +180,38 @@ github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127 h1:qwcF+vdFrvPSEUDSX5R github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= github.com/ethereum/go-ethereum v1.13.5 h1:U6TCRciCqZRe4FPXmy1sMGxTfuk8P7u2UoinF3VbaFk= github.com/ethereum/go-ethereum v1.13.5/go.mod h1:yMTu38GSuyxaYzQMViqNmQ1s3cE84abZexQmTgenWk0= -github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= -github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= 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/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/getkin/kin-openapi v0.124.0 h1:VSFNMB9C9rTKBnQ/fpyDU8ytMTr4dWI9QovSKj9kz/M= github.com/getkin/kin-openapi v0.124.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= -github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= -github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s= github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= 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= @@ -275,14 +231,11 @@ github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicbw= github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= @@ -301,25 +254,19 @@ github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= -github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= -github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= -github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= -github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/glog v1.1.1 h1:jxpi2eWoU84wbX9iIEyAeeoac3FLuifZpY9tcNUD9kw= +github.com/golang/glog v1.1.1/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -352,11 +299,8 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= -github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -372,7 +316,6 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= @@ -395,8 +338,8 @@ github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -407,25 +350,19 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGa github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0= -github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw= github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= @@ -435,17 +372,8 @@ github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZm github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= -github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= -github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/influxdb-client-go/v2 v2.4.0 h1:HGBfZYStlx3Kqvsv1h2pJixbCl/jhnFtxpKFAv9Tu5k= -github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8= -github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c h1:qSHzRbhzK8RdXOsAdfDgO49TtqC1oZ+acxPrkfTxcCs= -github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU= -github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= @@ -522,10 +450,6 @@ github.com/ipld/go-codec-dagpb v1.6.0/go.mod h1:ANzFhfP2uMJxRBr8CE+WQWs5UsNa0pYt github.com/ipld/go-ipld-prime v0.9.1-0.20210324083106-dc342a9917db/go.mod h1:KvBLMr4PX1gWptgkzRjVZCrLmSGcZCb/jioOQwCqZN8= github.com/ipld/go-ipld-prime v0.20.0 h1:Ud3VwE9ClxpO2LkCYP7vWPc0Fo+dYdYzgxUJZ3uRG4g= github.com/ipld/go-ipld-prime v0.20.0/go.mod h1:PzqZ/ZR981eKbgdr3y2DJYeD/8bgMawdGVlJDE8kK+M= -github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= -github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= -github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI= -github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jaypipes/ghw v0.10.0 h1:UHu9UX08Py315iPojADFPOkmjTsNzHj4g4adsNKKteY= @@ -554,28 +478,15 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= -github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= -github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/karalabe/usb v0.0.2 h1:M6QQBNxF+CQ8OFvxrT90BA0qBOXymndZnk5q235mFc4= github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= -github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk= -github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U= -github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw= -github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= -github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= @@ -596,9 +507,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= -github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= -github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= @@ -631,25 +539,15 @@ github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cO github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded/go.mod h1:xkDdm+akniYxVT9KW1Y2Y7Hso6aW+rZObz3nrA9yTHw= github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 h1:4oH3NqV0NvcdS44Ld3zK2tO8IUiNozIggm74yobQeZg= github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18/go.mod h1:Jpf4jHK+fbWioBHRDRM1WadNT1qmY27g2YicTdO0Rtc= -github.com/livepeer/lpms v0.0.0-20240731140137-28406cf8bc78 h1:J026gPdu533qY+KNEgWaSiu3dExW+DB4UyE8+PAEKvg= -github.com/livepeer/lpms v0.0.0-20240731140137-28406cf8bc78/go.mod h1:z5ROP1l5OzAKSoqVRLc34MjUdueil6wHSecQYV7llIw= +github.com/livepeer/lpms v0.0.0-20240711175220-227325841434 h1:E7PKN6q/jMLapEV+eEwlwv87Xe5zacaVhvZ8T6AJR3c= +github.com/livepeer/lpms v0.0.0-20240711175220-227325841434/go.mod h1:Hr/JhxxPDipOVd4ZrGYWrdJfpVF8/SEI0nNr2ctAlkM= github.com/livepeer/m3u8 v0.11.1 h1:VkUJzfNTyjy9mqsgp5JPvouwna8wGZMvd/gAfT5FinU= github.com/livepeer/m3u8 v0.11.1/go.mod h1:IUqAtwWPAG2CblfQa4SVzTQoDcEMPyfNOaBSxqHMS04= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -663,13 +561,9 @@ github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.18 h1:JL0eqdCOq6DJVNPSvArO/bIV9/P7fbGrV00LZHc+5aI= github.com/mattn/go-sqlite3 v1.14.18/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg= -github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ= -github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= @@ -684,7 +578,6 @@ github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dz github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= @@ -711,7 +604,6 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwd github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= @@ -751,11 +643,6 @@ github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/n github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM= -github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= -github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oapi-codegen/nethttp-middleware v1.0.1 h1:ZWvwfnMU0eloHX1VEJmQscQm3741t0vCm0eSIie1NIo= github.com/oapi-codegen/nethttp-middleware v1.0.1/go.mod h1:P7xtAvpoqNB+5obR9qRCeefH7YlXWSK3KgPs/9WB8tE= github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= @@ -763,16 +650,11 @@ github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= -github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs= github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.22.1 h1:pY8O4lBfsHKZHM/6nrxkhVPUznOlIu3quZcKP/M20KI= github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -786,12 +668,10 @@ github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/ github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/peterbourgon/ff/v3 v3.4.0 h1:QBvM/rizZM1cB0p0lGMdmR7HxZeI/ZrBWB4DqLkMUBc= @@ -800,9 +680,6 @@ github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQm github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= -github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -854,21 +731,16 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= @@ -889,12 +761,7 @@ github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+ github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= @@ -929,10 +796,8 @@ github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+F github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= @@ -942,12 +807,6 @@ github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8= github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= -github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/vincent-petithory/dataurl v1.0.0 h1:cXw+kPto8NLuJtlMsI152irrVw9fRDX8AbShPRpg2CI= github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= @@ -961,16 +820,8 @@ github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvS github.com/whyrusleeping/cbor-gen v0.0.0-20200123233031-1cdf64d27158/go.mod h1:Xj/M2wWU+QdTdRbu/L/1dIZY8/Wb2K9pAhtroQuxJJI= github.com/whyrusleeping/cbor-gen v0.0.0-20230418232409-daab9ece03a0 h1:XYEgH2nJgsrcrj32p+SAbx6T3s/6QknOXezXtz7kzbg= github.com/whyrusleeping/cbor-gen v0.0.0-20230418232409-daab9ece03a0/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= -github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= -github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= -github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= -github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1010,17 +861,13 @@ go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= @@ -1066,11 +913,9 @@ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -1079,7 +924,6 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1090,16 +934,13 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -1117,8 +958,8 @@ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= -golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= +golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1138,10 +979,8 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1150,16 +989,11 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1175,13 +1009,10 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1221,7 +1052,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= @@ -1231,21 +1061,16 @@ golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180810170437-e96c4e24768d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -1322,7 +1147,6 @@ google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCID google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1355,11 +1179,10 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao= google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= -google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= -google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= -google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM= +google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1377,8 +1200,8 @@ google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTp google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc v1.57.1 h1:upNTNqv0ES+2ZOOqACwVtS3Il8M12/+Hz41RCPzAjQg= +google.golang.org/grpc v1.57.1/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1394,9 +1217,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1405,14 +1227,10 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= -gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/install_ffmpeg.sh b/install_ffmpeg.sh index 3de15fdc1..df864b496 100755 --- a/install_ffmpeg.sh +++ b/install_ffmpeg.sh @@ -1,3 +1,230 @@ #!/usr/bin/env bash -echo 'WARNING: downloading and executing lpms/install_ffmpeg.sh, use it directly in case of issues' -curl https://raw.githubusercontent.com/livepeer/lpms/5b7b9f5e831f041c6cf707bbaad7b5503c2f138d/install_ffmpeg.sh | bash -s $1 + +set -exuo pipefail + +ROOT="${1:-$HOME}" +NPROC=${NPROC:-$(nproc)} +EXTRA_CFLAGS="" +EXTRA_LDFLAGS="" +EXTRA_X264_FLAGS="" +EXTRA_FFMPEG_FLAGS="" +BUILD_TAGS="${BUILD_TAGS:-}" + +# Build platform flags +BUILDOS=$(uname -s | tr '[:upper:]' '[:lower:]') +BUILDARCH=$(uname -m | tr '[:upper:]' '[:lower:]') +if [[ $BUILDARCH == "aarch64" ]]; then + BUILDARCH=arm64 +fi +if [[ $BUILDARCH == "x86_64" ]]; then + BUILDARCH=amd64 +fi + +# Override these for cross-compilation +export GOOS="${GOOS:-$BUILDOS}" +export GOARCH="${GOARCH:-$BUILDARCH}" + +echo "BUILDOS: $BUILDOS" +echo "BUILDARCH: $BUILDARCH" +echo "GOOS: $GOOS" +echo "GOARCH: $GOARCH" + +function check_sysroot() { + if ! stat $SYSROOT > /dev/null; then + echo "cross-compilation sysroot not found at $SYSROOT, try setting SYSROOT to the correct path" + exit 1 + fi +} + +if [[ "$BUILDARCH" == "amd64" && "$BUILDOS" == "linux" && "$GOARCH" == "arm64" && "$GOOS" == "linux" ]]; then + echo "cross-compiling linux-amd64 --> linux-arm64" + export CC="clang-14" + export STRIP="llvm-strip-14" + export AR="llvm-ar-14" + export RANLIB="llvm-ranlib-14" + EXTRA_CFLAGS="--target=aarch64-linux-gnu -I/usr/local/cuda_arm64/include $EXTRA_CFLAGS" + EXTRA_LDFLAGS="-fuse-ld=lld --target=aarch64-linux-gnu -L/usr/local/cuda_arm64/lib64 $EXTRA_LDFLAGS" + EXTRA_FFMPEG_FLAGS="$EXTRA_FFMPEG_FLAGS --arch=aarch64 --enable-cross-compile --cc=clang --strip=llvm-strip-14" + HOST_OS="--host=aarch64-linux-gnu" +fi + +if [[ "$BUILDARCH" == "arm64" && "$BUILDOS" == "darwin" && "$GOARCH" == "arm64" && "$GOOS" == "linux" ]]; then + SYSROOT="${SYSROOT:-"/tmp/sysroot-aarch64-linux-gnu"}" + check_sysroot + echo "cross-compiling darwin-arm64 --> linux-arm64" + LLVM_PATH="${LLVM_PATH:-/opt/homebrew/opt/llvm/bin}" + if [[ ! -f "$LLVM_PATH/ld.lld" ]]; then + echo "llvm linker not found at '$LLVM_PATH/ld.lld'. try 'brew install llvm' or set LLVM_PATH to your LLVM bin directory" + exit 1 + fi + export CC="$LLVM_PATH/clang --sysroot=$SYSROOT" + export AR="/opt/homebrew/opt/llvm/bin/llvm-ar" + export RANLIB="/opt/homebrew/opt/llvm/bin/llvm-ranlib" + EXTRA_CFLAGS="--target=aarch64-linux-gnu $EXTRA_CFLAGS" + EXTRA_LDFLAGS="--target=aarch64-linux-gnu -fuse-ld=$LLVM_PATH/ld.lld $EXTRA_LDFLAGS" + EXTRA_FFMPEG_FLAGS="$EXTRA_FFMPEG_FLAGS --arch=aarch64 --enable-cross-compile --cc=$LLVM_PATH/clang --sysroot=$SYSROOT --ar=$AR --ranlib=$RANLIB --target-os=linux" + EXTRA_X264_FLAGS="$EXTRA_X264_FLAGS --sysroot=$SYSROOT --ar=$AR --ranlib=$RANLIB" + HOST_OS="--host=aarch64-linux-gnu" +fi + +if [[ "$BUILDOS" == "linux" && "$GOARCH" == "amd64" && "$GOOS" == "windows" ]]; then + echo "cross-compiling linux-$BUILDARCH --> windows-amd64" + SYSROOT="${SYSROOT:-"/usr/x86_64-w64-mingw32"}" + check_sysroot + EXTRA_CFLAGS="-L$SYSROOT/lib -I$SYSROOT/include $EXTRA_CFLAGS" + EXTRA_LDFLAGS="-L$SYSROOT/lib $EXTRA_LDFLAGS" + EXTRA_FFMPEG_FLAGS="$EXTRA_FFMPEG_FLAGS --arch=x86_64 --enable-cross-compile --cross-prefix=x86_64-w64-mingw32- --target-os=mingw64 --sysroot=$SYSROOT" + EXTRA_X264_FLAGS="$EXTRA_X264_FLAGS --cross-prefix=x86_64-w64-mingw32- --sysroot=$SYSROOT" + HOST_OS="--host=mingw64" + # Workaround for https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=967969 + export PKG_CONFIG_LIBDIR="/usr/local/x86_64-w64-mingw32/lib/pkgconfig" + EXTRA_FFMPEG_FLAGS="$EXTRA_FFMPEG_FLAGS --pkg-config=$(which pkg-config)" +fi + +if [[ "$BUILDARCH" == "amd64" && "$BUILDOS" == "darwin" && "$GOARCH" == "arm64" && "$GOOS" == "darwin" ]]; then + echo "cross-compiling darwin-amd64 --> darwin-arm64" + EXTRA_CFLAGS="$EXTRA_CFLAGS --target=arm64-apple-macos11" + EXTRA_LDFLAGS="$EXTRA_LDFLAGS --target=arm64-apple-macos11" + HOST_OS="--host=aarch64-darwin" + EXTRA_FFMPEG_FLAGS="$EXTRA_FFMPEG_FLAGS --arch=aarch64 --enable-cross-compile" +fi + +# Windows (MSYS2) needs a few tweaks +if [[ "$BUILDOS" == *"MSYS"* ]]; then + ROOT="/build" + export PATH="$PATH:/usr/bin:/mingw64/bin" + export C_INCLUDE_PATH="${C_INCLUDE_PATH:-}:/mingw64/lib" + + export PATH="$ROOT/compiled/bin":$PATH + export PKG_CONFIG_PATH=/mingw64/lib/pkgconfig + + export TARGET_OS="--target-os=mingw64" + export HOST_OS="--host=x86_64-w64-mingw32" + export BUILD_OS="--build=x86_64-w64-mingw32 --host=x86_64-w64-mingw32 --target=x86_64-w64-mingw32" + + # Needed for mbedtls + export WINDOWS_BUILD=1 +fi + +export PATH="$ROOT/compiled/bin:${PATH}" +export PKG_CONFIG_PATH="${PKG_CONFIG_PATH:-}:$ROOT/compiled/lib/pkgconfig" + +mkdir -p "$ROOT/" + +# NVENC only works on Windows/Linux +if [[ "$GOOS" != "darwin" ]]; then + if [[ ! -e "$ROOT/nv-codec-headers" ]]; then + git clone https://git.videolan.org/git/ffmpeg/nv-codec-headers.git "$ROOT/nv-codec-headers" + cd $ROOT/nv-codec-headers + git checkout n12.1.14.0 + make -e PREFIX="$ROOT/compiled" + make install -e PREFIX="$ROOT/compiled" + fi +fi + +if [[ "$GOOS" != "windows" && "$GOARCH" == "amd64" ]]; then + if [[ ! -e "$ROOT/nasm-2.14.02" ]]; then + # sudo apt-get -y install asciidoc xmlto # this fails :( + cd "$ROOT" + curl -o nasm-2.14.02.tar.gz https://www.nasm.us/pub/nasm/releasebuilds/2.14.02/nasm-2.14.02.tar.gz + echo 'b34bae344a3f2ed93b2ca7bf25f1ed3fb12da89eeda6096e3551fd66adeae9fc nasm-2.14.02.tar.gz' >nasm-2.14.02.tar.gz.sha256 + sha256sum -c nasm-2.14.02.tar.gz.sha256 + tar xf nasm-2.14.02.tar.gz + rm nasm-2.14.02.tar.gz nasm-2.14.02.tar.gz.sha256 + cd "$ROOT/nasm-2.14.02" + ./configure --prefix="$ROOT/compiled" + make -j$NPROC + make -j$NPROC install || echo "Installing docs fails but should be OK otherwise" + fi +fi + +if [[ ! -e "$ROOT/x264" ]]; then + git clone http://git.videolan.org/git/x264.git "$ROOT/x264" + cd "$ROOT/x264" + if [[ $GOARCH == "arm64" ]]; then + # newer git master, compiles on Apple Silicon + git checkout 66a5bc1bd1563d8227d5d18440b525a09bcf17ca + else + # older git master, does not compile on Apple Silicon + git checkout 545de2ffec6ae9a80738de1b2c8cf820249a2530 + fi + ./configure --prefix="$ROOT/compiled" --enable-pic --enable-static ${HOST_OS:-} --disable-cli --extra-cflags="$EXTRA_CFLAGS" --extra-asflags="$EXTRA_CFLAGS" --extra-ldflags="$EXTRA_LDFLAGS" $EXTRA_X264_FLAGS || (cat $ROOT/x264/config.log && exit 1) + make -j$NPROC + make -j$NPROC install-lib-static +fi + +if [[ "$GOOS" == "linux" && "$BUILD_TAGS" == *"debug-video"* ]]; then + sudo apt-get install -y libnuma-dev cmake + if [[ ! -e "$ROOT/x265" ]]; then + git clone https://bitbucket.org/multicoreware/x265_git.git "$ROOT/x265" + cd "$ROOT/x265" + git checkout 17839cc0dc5a389e27810944ae2128a65ac39318 + cd build/linux/ + cmake -DCMAKE_INSTALL_PREFIX=$ROOT/compiled -G "Unix Makefiles" ../../source + make -j$NPROC + make -j$NPROC install + fi + # VP8/9 support + if [[ ! -e "$ROOT/libvpx" ]]; then + git clone https://chromium.googlesource.com/webm/libvpx.git "$ROOT/libvpx" + cd "$ROOT/libvpx" + git checkout ab35ee100a38347433af24df05a5e1578172a2ae + ./configure --prefix="$ROOT/compiled" --disable-examples --disable-unit-tests --enable-vp9-highbitdepth --enable-shared --as=nasm + make -j$NPROC + make -j$NPROC install + fi +fi + +DISABLE_FFMPEG_COMPONENTS="" +EXTRA_FFMPEG_LDFLAGS="$EXTRA_LDFLAGS" +# all flags which should be present for production build, but should be replaced/removed for debug build +DEV_FFMPEG_FLAGS="" + +if [[ "$BUILDOS" == "darwin" && "$GOOS" == "darwin" ]]; then + EXTRA_FFMPEG_LDFLAGS="$EXTRA_FFMPEG_LDFLAGS -framework CoreFoundation -framework Security" +elif [[ "$GOOS" == "windows" ]]; then + EXTRA_FFMPEG_FLAGS="$EXTRA_FFMPEG_FLAGS --enable-cuda --enable-cuda-llvm --enable-cuvid --enable-nvenc --enable-decoder=h264_cuvid,hevc_cuvid,vp8_cuvid,vp9_cuvid --enable-filter=scale_cuda,signature_cuda,hwupload_cuda --enable-encoder=h264_nvenc,hevc_nvenc" +elif [[ -e "/usr/local/cuda/lib64" ]]; then + echo "CUDA SDK detected, building with GPU support" + EXTRA_FFMPEG_FLAGS="$EXTRA_FFMPEG_FLAGS --enable-nonfree --enable-cuda-nvcc --enable-libnpp --enable-cuda --enable-cuda-llvm --enable-cuvid --enable-nvenc --enable-decoder=h264_cuvid,hevc_cuvid,vp8_cuvid,vp9_cuvid --enable-filter=scale_npp,signature_cuda,hwupload_cuda --enable-encoder=h264_nvenc,hevc_nvenc" +else + echo "No CUDA SDK detected, building without GPU support" +fi + +if [[ $BUILD_TAGS == *"debug-video"* ]]; then + echo "video debug mode, building ffmpeg with tools, debug info and additional capabilities for running tests" + DEV_FFMPEG_FLAGS="--enable-muxer=md5,flv --enable-demuxer=hls --enable-filter=ssim,tinterlace --enable-encoder=wrapped_avframe,pcm_s16le " + DEV_FFMPEG_FLAGS+="--enable-shared --enable-debug=3 --disable-stripping --disable-optimizations --enable-encoder=libx265,libvpx_vp8,libvpx_vp9 " + DEV_FFMPEG_FLAGS+="--enable-decoder=hevc,libvpx_vp8,libvpx_vp9 --enable-libx265 --enable-libvpx --enable-bsf=noise " +else + # disable all unnecessary features for production build + DISABLE_FFMPEG_COMPONENTS+=" --disable-doc --disable-sdl2 --disable-iconv --disable-muxers --disable-demuxers --disable-parsers --disable-protocols " + DISABLE_FFMPEG_COMPONENTS+=" --disable-encoders --disable-decoders --disable-filters --disable-bsfs --disable-postproc --disable-lzma " +fi + +if [[ ! -e "$ROOT/ffmpeg/libavcodec/libavcodec.a" ]]; then + git clone https://github.com/livepeer/FFmpeg-6.1.1.git "$ROOT/ffmpeg" || echo "FFmpeg dir already exists" + cd "$ROOT/ffmpeg" + ./configure ${TARGET_OS:-} $DISABLE_FFMPEG_COMPONENTS --fatal-warnings \ + --enable-libx264 --enable-gpl \ + --enable-protocol=rtmp,file,pipe \ + --enable-muxer=mp3,wav,flac,mpegts,hls,segment,mp4,hevc,matroska,webm,null --enable-demuxer=mp3,wav,flac,flv,mpegts,mp4,mov,webm,matroska,image2 \ + --enable-bsf=h264_mp4toannexb,aac_adtstoasc,h264_metadata,h264_redundant_pps,hevc_mp4toannexb,extract_extradata \ + --enable-parser=mpegaudio,vorbis,opus,flac,aac,aac_latm,h264,hevc,vp8,vp9,png \ + --enable-filter=abuffer,buffer,abuffersink,buffersink,afifo,fifo,aformat,format \ + --enable-filter=aresample,asetnsamples,fps,scale,hwdownload,select,livepeer_dnn,signature \ + --enable-encoder=mp3,vorbis,flac,aac,opus,libx264 \ + --enable-decoder=mp3,vorbis,flac,aac,opus,h264,png \ + --extra-cflags="${EXTRA_CFLAGS} -I${ROOT}/compiled/include -I/usr/local/cuda/include" \ + --extra-ldflags="${EXTRA_FFMPEG_LDFLAGS} -L${ROOT}/compiled/lib -L/usr/local/cuda/lib64" \ + --prefix="$ROOT/compiled" \ + $EXTRA_FFMPEG_FLAGS \ + $DEV_FFMPEG_FLAGS || (tail -100 ${ROOT}/ffmpeg/ffbuild/config.log && exit 1) + # If configure fails, then print the last 100 log lines for debugging and exit. +fi + +if [[ ! -e "$ROOT/ffmpeg/libavcodec/libavcodec.a" || $BUILD_TAGS == *"debug-video"* ]]; then + cd "$ROOT/ffmpeg" + make -j$NPROC + make -j$NPROC install +fi diff --git a/monitor/census.go b/monitor/census.go index 12ab13327..2f883d5e4 100644 --- a/monitor/census.go +++ b/monitor/census.go @@ -1629,12 +1629,14 @@ func Reserve(sender string, reserve *big.Int) { } func MaxTranscodingPrice(maxPrice *big.Rat) { - floatWei, _ := maxPrice.Float64() - if err := stats.RecordWithTags(census.ctx, - []tag.Mutator{tag.Insert(census.kSender, "max")}, - census.mTranscodingPrice.M(floatWei)); err != nil { + floatWei, ok := maxPrice.Float64() + if ok { + if err := stats.RecordWithTags(census.ctx, + []tag.Mutator{tag.Insert(census.kSender, "max")}, + census.mTranscodingPrice.M(floatWei)); err != nil { - glog.Errorf("Error recording metrics err=%q", err) + glog.Errorf("Error recording metrics err=%q", err) + } } } @@ -1748,13 +1750,15 @@ func MaxGasPrice(maxGasPrice *big.Int) { // TranscodingPrice records the last transcoding price func TranscodingPrice(sender string, price *big.Rat) { - floatWei, _ := price.Float64() - stats.Record(census.ctx, census.mTranscodingPrice.M(floatWei)) - if err := stats.RecordWithTags(census.ctx, - []tag.Mutator{tag.Insert(census.kSender, sender)}, - census.mTranscodingPrice.M(floatWei)); err != nil { + floatWei, ok := price.Float64() + if ok { + stats.Record(census.ctx, census.mTranscodingPrice.M(floatWei)) + if err := stats.RecordWithTags(census.ctx, + []tag.Mutator{tag.Insert(census.kSender, sender)}, + census.mTranscodingPrice.M(floatWei)); err != nil { - glog.Errorf("Error recording metrics err=%q", err) + glog.Errorf("Error recording metrics err=%q", err) + } } } diff --git a/net/lp_rpc.pb.go b/net/lp_rpc.pb.go index 461cbfb79..0ccebb681 100644 --- a/net/lp_rpc.pb.go +++ b/net/lp_rpc.pb.go @@ -1,24 +1,24 @@ // Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v4.25.2 // source: net/lp_rpc.proto package net import ( - fmt "fmt" - proto "github.com/golang/protobuf/proto" - math "math" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) type OSInfo_StorageType int32 @@ -28,24 +28,45 @@ const ( OSInfo_GOOGLE OSInfo_StorageType = 2 ) -var OSInfo_StorageType_name = map[int32]string{ - 0: "DIRECT", - 1: "S3", - 2: "GOOGLE", -} +// Enum value maps for OSInfo_StorageType. +var ( + OSInfo_StorageType_name = map[int32]string{ + 0: "DIRECT", + 1: "S3", + 2: "GOOGLE", + } + OSInfo_StorageType_value = map[string]int32{ + "DIRECT": 0, + "S3": 1, + "GOOGLE": 2, + } +) -var OSInfo_StorageType_value = map[string]int32{ - "DIRECT": 0, - "S3": 1, - "GOOGLE": 2, +func (x OSInfo_StorageType) Enum() *OSInfo_StorageType { + p := new(OSInfo_StorageType) + *p = x + return p } func (x OSInfo_StorageType) String() string { - return proto.EnumName(OSInfo_StorageType_name, int32(x)) + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (OSInfo_StorageType) Descriptor() protoreflect.EnumDescriptor { + return file_net_lp_rpc_proto_enumTypes[0].Descriptor() +} + +func (OSInfo_StorageType) Type() protoreflect.EnumType { + return &file_net_lp_rpc_proto_enumTypes[0] +} + +func (x OSInfo_StorageType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) } +// Deprecated: Use OSInfo_StorageType.Descriptor instead. func (OSInfo_StorageType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{4, 0} + return file_net_lp_rpc_proto_rawDescGZIP(), []int{4, 0} } // Desired output format @@ -56,22 +77,43 @@ const ( VideoProfile_MP4 VideoProfile_Format = 1 ) -var VideoProfile_Format_name = map[int32]string{ - 0: "MPEGTS", - 1: "MP4", -} +// Enum value maps for VideoProfile_Format. +var ( + VideoProfile_Format_name = map[int32]string{ + 0: "MPEGTS", + 1: "MP4", + } + VideoProfile_Format_value = map[string]int32{ + "MPEGTS": 0, + "MP4": 1, + } +) -var VideoProfile_Format_value = map[string]int32{ - "MPEGTS": 0, - "MP4": 1, +func (x VideoProfile_Format) Enum() *VideoProfile_Format { + p := new(VideoProfile_Format) + *p = x + return p } func (x VideoProfile_Format) String() string { - return proto.EnumName(VideoProfile_Format_name, int32(x)) + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (VideoProfile_Format) Descriptor() protoreflect.EnumDescriptor { + return file_net_lp_rpc_proto_enumTypes[1].Descriptor() +} + +func (VideoProfile_Format) Type() protoreflect.EnumType { + return &file_net_lp_rpc_proto_enumTypes[1] +} + +func (x VideoProfile_Format) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) } +// Deprecated: Use VideoProfile_Format.Descriptor instead. func (VideoProfile_Format) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{12, 0} + return file_net_lp_rpc_proto_rawDescGZIP(), []int{12, 0} } type VideoProfile_Profile int32 @@ -84,28 +126,49 @@ const ( VideoProfile_H264_CONSTRAINED_HIGH VideoProfile_Profile = 4 ) -var VideoProfile_Profile_name = map[int32]string{ - 0: "ENCODER_DEFAULT", - 1: "H264_BASELINE", - 2: "H264_MAIN", - 3: "H264_HIGH", - 4: "H264_CONSTRAINED_HIGH", -} +// Enum value maps for VideoProfile_Profile. +var ( + VideoProfile_Profile_name = map[int32]string{ + 0: "ENCODER_DEFAULT", + 1: "H264_BASELINE", + 2: "H264_MAIN", + 3: "H264_HIGH", + 4: "H264_CONSTRAINED_HIGH", + } + VideoProfile_Profile_value = map[string]int32{ + "ENCODER_DEFAULT": 0, + "H264_BASELINE": 1, + "H264_MAIN": 2, + "H264_HIGH": 3, + "H264_CONSTRAINED_HIGH": 4, + } +) -var VideoProfile_Profile_value = map[string]int32{ - "ENCODER_DEFAULT": 0, - "H264_BASELINE": 1, - "H264_MAIN": 2, - "H264_HIGH": 3, - "H264_CONSTRAINED_HIGH": 4, +func (x VideoProfile_Profile) Enum() *VideoProfile_Profile { + p := new(VideoProfile_Profile) + *p = x + return p } func (x VideoProfile_Profile) String() string { - return proto.EnumName(VideoProfile_Profile_name, int32(x)) + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (VideoProfile_Profile) Descriptor() protoreflect.EnumDescriptor { + return file_net_lp_rpc_proto_enumTypes[2].Descriptor() +} + +func (VideoProfile_Profile) Type() protoreflect.EnumType { + return &file_net_lp_rpc_proto_enumTypes[2] +} + +func (x VideoProfile_Profile) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) } +// Deprecated: Use VideoProfile_Profile.Descriptor instead. func (VideoProfile_Profile) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{12, 1} + return file_net_lp_rpc_proto_rawDescGZIP(), []int{12, 1} } type VideoProfile_VideoCodec int32 @@ -117,26 +180,47 @@ const ( VideoProfile_VP9 VideoProfile_VideoCodec = 3 ) -var VideoProfile_VideoCodec_name = map[int32]string{ - 0: "H264", - 1: "H265", - 2: "VP8", - 3: "VP9", -} +// Enum value maps for VideoProfile_VideoCodec. +var ( + VideoProfile_VideoCodec_name = map[int32]string{ + 0: "H264", + 1: "H265", + 2: "VP8", + 3: "VP9", + } + VideoProfile_VideoCodec_value = map[string]int32{ + "H264": 0, + "H265": 1, + "VP8": 2, + "VP9": 3, + } +) -var VideoProfile_VideoCodec_value = map[string]int32{ - "H264": 0, - "H265": 1, - "VP8": 2, - "VP9": 3, +func (x VideoProfile_VideoCodec) Enum() *VideoProfile_VideoCodec { + p := new(VideoProfile_VideoCodec) + *p = x + return p } func (x VideoProfile_VideoCodec) String() string { - return proto.EnumName(VideoProfile_VideoCodec_name, int32(x)) + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (VideoProfile_VideoCodec) Descriptor() protoreflect.EnumDescriptor { + return file_net_lp_rpc_proto_enumTypes[3].Descriptor() +} + +func (VideoProfile_VideoCodec) Type() protoreflect.EnumType { + return &file_net_lp_rpc_proto_enumTypes[3] } +func (x VideoProfile_VideoCodec) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use VideoProfile_VideoCodec.Descriptor instead. func (VideoProfile_VideoCodec) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{12, 2} + return file_net_lp_rpc_proto_rawDescGZIP(), []int{12, 2} } type VideoProfile_ChromaSubsampling int32 @@ -147,194 +231,246 @@ const ( VideoProfile_CHROMA_444 VideoProfile_ChromaSubsampling = 2 ) -var VideoProfile_ChromaSubsampling_name = map[int32]string{ - 0: "CHROMA_420", - 1: "CHROMA_422", - 2: "CHROMA_444", -} +// Enum value maps for VideoProfile_ChromaSubsampling. +var ( + VideoProfile_ChromaSubsampling_name = map[int32]string{ + 0: "CHROMA_420", + 1: "CHROMA_422", + 2: "CHROMA_444", + } + VideoProfile_ChromaSubsampling_value = map[string]int32{ + "CHROMA_420": 0, + "CHROMA_422": 1, + "CHROMA_444": 2, + } +) -var VideoProfile_ChromaSubsampling_value = map[string]int32{ - "CHROMA_420": 0, - "CHROMA_422": 1, - "CHROMA_444": 2, +func (x VideoProfile_ChromaSubsampling) Enum() *VideoProfile_ChromaSubsampling { + p := new(VideoProfile_ChromaSubsampling) + *p = x + return p } func (x VideoProfile_ChromaSubsampling) String() string { - return proto.EnumName(VideoProfile_ChromaSubsampling_name, int32(x)) + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } -func (VideoProfile_ChromaSubsampling) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{12, 3} +func (VideoProfile_ChromaSubsampling) Descriptor() protoreflect.EnumDescriptor { + return file_net_lp_rpc_proto_enumTypes[4].Descriptor() } -type PingPong struct { - // Implementation defined - Value []byte `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (VideoProfile_ChromaSubsampling) Type() protoreflect.EnumType { + return &file_net_lp_rpc_proto_enumTypes[4] } -func (m *PingPong) Reset() { *m = PingPong{} } -func (m *PingPong) String() string { return proto.CompactTextString(m) } -func (*PingPong) ProtoMessage() {} -func (*PingPong) Descriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{0} +func (x VideoProfile_ChromaSubsampling) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) } -func (m *PingPong) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PingPong.Unmarshal(m, b) +// Deprecated: Use VideoProfile_ChromaSubsampling.Descriptor instead. +func (VideoProfile_ChromaSubsampling) EnumDescriptor() ([]byte, []int) { + return file_net_lp_rpc_proto_rawDescGZIP(), []int{12, 3} } -func (m *PingPong) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PingPong.Marshal(b, m, deterministic) + +type PingPong struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Implementation defined + Value []byte `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` } -func (m *PingPong) XXX_Merge(src proto.Message) { - xxx_messageInfo_PingPong.Merge(m, src) + +func (x *PingPong) Reset() { + *x = PingPong{} + if protoimpl.UnsafeEnabled { + mi := &file_net_lp_rpc_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *PingPong) XXX_Size() int { - return xxx_messageInfo_PingPong.Size(m) + +func (x *PingPong) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *PingPong) XXX_DiscardUnknown() { - xxx_messageInfo_PingPong.DiscardUnknown(m) + +func (*PingPong) ProtoMessage() {} + +func (x *PingPong) ProtoReflect() protoreflect.Message { + mi := &file_net_lp_rpc_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_PingPong proto.InternalMessageInfo +// Deprecated: Use PingPong.ProtoReflect.Descriptor instead. +func (*PingPong) Descriptor() ([]byte, []int) { + return file_net_lp_rpc_proto_rawDescGZIP(), []int{0} +} -func (m *PingPong) GetValue() []byte { - if m != nil { - return m.Value +func (x *PingPong) GetValue() []byte { + if x != nil { + return x.Value } return nil } // sent by Broadcaster to Orchestrator to terminate the transcoding session and free resources (used for verification sessions) type EndTranscodingSessionRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Data for transcoding authentication - AuthToken *AuthToken `protobuf:"bytes,1,opt,name=auth_token,json=authToken,proto3" json:"auth_token,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + AuthToken *AuthToken `protobuf:"bytes,1,opt,name=auth_token,json=authToken,proto3" json:"auth_token,omitempty"` } -func (m *EndTranscodingSessionRequest) Reset() { *m = EndTranscodingSessionRequest{} } -func (m *EndTranscodingSessionRequest) String() string { return proto.CompactTextString(m) } -func (*EndTranscodingSessionRequest) ProtoMessage() {} -func (*EndTranscodingSessionRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{1} +func (x *EndTranscodingSessionRequest) Reset() { + *x = EndTranscodingSessionRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_net_lp_rpc_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *EndTranscodingSessionRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_EndTranscodingSessionRequest.Unmarshal(m, b) -} -func (m *EndTranscodingSessionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_EndTranscodingSessionRequest.Marshal(b, m, deterministic) +func (x *EndTranscodingSessionRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *EndTranscodingSessionRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_EndTranscodingSessionRequest.Merge(m, src) -} -func (m *EndTranscodingSessionRequest) XXX_Size() int { - return xxx_messageInfo_EndTranscodingSessionRequest.Size(m) -} -func (m *EndTranscodingSessionRequest) XXX_DiscardUnknown() { - xxx_messageInfo_EndTranscodingSessionRequest.DiscardUnknown(m) + +func (*EndTranscodingSessionRequest) ProtoMessage() {} + +func (x *EndTranscodingSessionRequest) ProtoReflect() protoreflect.Message { + mi := &file_net_lp_rpc_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_EndTranscodingSessionRequest proto.InternalMessageInfo +// Deprecated: Use EndTranscodingSessionRequest.ProtoReflect.Descriptor instead. +func (*EndTranscodingSessionRequest) Descriptor() ([]byte, []int) { + return file_net_lp_rpc_proto_rawDescGZIP(), []int{1} +} -func (m *EndTranscodingSessionRequest) GetAuthToken() *AuthToken { - if m != nil { - return m.AuthToken +func (x *EndTranscodingSessionRequest) GetAuthToken() *AuthToken { + if x != nil { + return x.AuthToken } return nil } type EndTranscodingSessionResponse struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields } -func (m *EndTranscodingSessionResponse) Reset() { *m = EndTranscodingSessionResponse{} } -func (m *EndTranscodingSessionResponse) String() string { return proto.CompactTextString(m) } -func (*EndTranscodingSessionResponse) ProtoMessage() {} -func (*EndTranscodingSessionResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{2} +func (x *EndTranscodingSessionResponse) Reset() { + *x = EndTranscodingSessionResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_net_lp_rpc_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *EndTranscodingSessionResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_EndTranscodingSessionResponse.Unmarshal(m, b) -} -func (m *EndTranscodingSessionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_EndTranscodingSessionResponse.Marshal(b, m, deterministic) -} -func (m *EndTranscodingSessionResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_EndTranscodingSessionResponse.Merge(m, src) +func (x *EndTranscodingSessionResponse) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *EndTranscodingSessionResponse) XXX_Size() int { - return xxx_messageInfo_EndTranscodingSessionResponse.Size(m) -} -func (m *EndTranscodingSessionResponse) XXX_DiscardUnknown() { - xxx_messageInfo_EndTranscodingSessionResponse.DiscardUnknown(m) + +func (*EndTranscodingSessionResponse) ProtoMessage() {} + +func (x *EndTranscodingSessionResponse) ProtoReflect() protoreflect.Message { + mi := &file_net_lp_rpc_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_EndTranscodingSessionResponse proto.InternalMessageInfo +// Deprecated: Use EndTranscodingSessionResponse.ProtoReflect.Descriptor instead. +func (*EndTranscodingSessionResponse) Descriptor() ([]byte, []int) { + return file_net_lp_rpc_proto_rawDescGZIP(), []int{2} +} // This request is sent by the broadcaster in `GetTranscoder` to request // information on which transcoder to use. type OrchestratorRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Ethereum address of the broadcaster Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` // Broadcaster's signature over its address Sig []byte `protobuf:"bytes,2,opt,name=sig,proto3" json:"sig,omitempty"` // Features and constraints required by the broadcaster - Capabilities *Capabilities `protobuf:"bytes,3,opt,name=capabilities,proto3" json:"capabilities,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Capabilities *Capabilities `protobuf:"bytes,3,opt,name=capabilities,proto3" json:"capabilities,omitempty"` } -func (m *OrchestratorRequest) Reset() { *m = OrchestratorRequest{} } -func (m *OrchestratorRequest) String() string { return proto.CompactTextString(m) } -func (*OrchestratorRequest) ProtoMessage() {} -func (*OrchestratorRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{3} +func (x *OrchestratorRequest) Reset() { + *x = OrchestratorRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_net_lp_rpc_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *OrchestratorRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_OrchestratorRequest.Unmarshal(m, b) -} -func (m *OrchestratorRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_OrchestratorRequest.Marshal(b, m, deterministic) -} -func (m *OrchestratorRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_OrchestratorRequest.Merge(m, src) -} -func (m *OrchestratorRequest) XXX_Size() int { - return xxx_messageInfo_OrchestratorRequest.Size(m) +func (x *OrchestratorRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *OrchestratorRequest) XXX_DiscardUnknown() { - xxx_messageInfo_OrchestratorRequest.DiscardUnknown(m) + +func (*OrchestratorRequest) ProtoMessage() {} + +func (x *OrchestratorRequest) ProtoReflect() protoreflect.Message { + mi := &file_net_lp_rpc_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_OrchestratorRequest proto.InternalMessageInfo +// Deprecated: Use OrchestratorRequest.ProtoReflect.Descriptor instead. +func (*OrchestratorRequest) Descriptor() ([]byte, []int) { + return file_net_lp_rpc_proto_rawDescGZIP(), []int{3} +} -func (m *OrchestratorRequest) GetAddress() []byte { - if m != nil { - return m.Address +func (x *OrchestratorRequest) GetAddress() []byte { + if x != nil { + return x.Address } return nil } -func (m *OrchestratorRequest) GetSig() []byte { - if m != nil { - return m.Sig +func (x *OrchestratorRequest) GetSig() []byte { + if x != nil { + return x.Sig } return nil } -func (m *OrchestratorRequest) GetCapabilities() *Capabilities { - if m != nil { - return m.Capabilities +func (x *OrchestratorRequest) GetCapabilities() *Capabilities { + if x != nil { + return x.Capabilities } return nil } @@ -342,54 +478,66 @@ func (m *OrchestratorRequest) GetCapabilities() *Capabilities { // OSInfo needed to negotiate storages that will be used. // It carries info needed to write to the storage. type OSInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Storage type: direct, s3, ipfs. - StorageType OSInfo_StorageType `protobuf:"varint,1,opt,name=storageType,proto3,enum=net.OSInfo_StorageType" json:"storageType,omitempty"` - S3Info *S3OSInfo `protobuf:"bytes,16,opt,name=s3info,proto3" json:"s3info,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + StorageType OSInfo_StorageType `protobuf:"varint,1,opt,name=storageType,proto3,enum=net.OSInfo_StorageType" json:"storageType,omitempty"` + S3Info *S3OSInfo `protobuf:"bytes,16,opt,name=s3info,proto3" json:"s3info,omitempty"` } -func (m *OSInfo) Reset() { *m = OSInfo{} } -func (m *OSInfo) String() string { return proto.CompactTextString(m) } -func (*OSInfo) ProtoMessage() {} -func (*OSInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{4} +func (x *OSInfo) Reset() { + *x = OSInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_net_lp_rpc_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *OSInfo) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_OSInfo.Unmarshal(m, b) -} -func (m *OSInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_OSInfo.Marshal(b, m, deterministic) +func (x *OSInfo) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *OSInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_OSInfo.Merge(m, src) -} -func (m *OSInfo) XXX_Size() int { - return xxx_messageInfo_OSInfo.Size(m) -} -func (m *OSInfo) XXX_DiscardUnknown() { - xxx_messageInfo_OSInfo.DiscardUnknown(m) + +func (*OSInfo) ProtoMessage() {} + +func (x *OSInfo) ProtoReflect() protoreflect.Message { + mi := &file_net_lp_rpc_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_OSInfo proto.InternalMessageInfo +// Deprecated: Use OSInfo.ProtoReflect.Descriptor instead. +func (*OSInfo) Descriptor() ([]byte, []int) { + return file_net_lp_rpc_proto_rawDescGZIP(), []int{4} +} -func (m *OSInfo) GetStorageType() OSInfo_StorageType { - if m != nil { - return m.StorageType +func (x *OSInfo) GetStorageType() OSInfo_StorageType { + if x != nil { + return x.StorageType } return OSInfo_DIRECT } -func (m *OSInfo) GetS3Info() *S3OSInfo { - if m != nil { - return m.S3Info +func (x *OSInfo) GetS3Info() *S3OSInfo { + if x != nil { + return x.S3Info } return nil } type S3OSInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Host to use to connect to S3 Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` // Key (prefix) to use when uploading the object. @@ -401,338 +549,223 @@ type S3OSInfo struct { // Needed for POST policy. Credential string `protobuf:"bytes,5,opt,name=credential,proto3" json:"credential,omitempty"` // Needed for POST policy. - XAmzDate string `protobuf:"bytes,6,opt,name=xAmzDate,proto3" json:"xAmzDate,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + XAmzDate string `protobuf:"bytes,6,opt,name=xAmzDate,proto3" json:"xAmzDate,omitempty"` } -func (m *S3OSInfo) Reset() { *m = S3OSInfo{} } -func (m *S3OSInfo) String() string { return proto.CompactTextString(m) } -func (*S3OSInfo) ProtoMessage() {} -func (*S3OSInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{5} +func (x *S3OSInfo) Reset() { + *x = S3OSInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_net_lp_rpc_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *S3OSInfo) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_S3OSInfo.Unmarshal(m, b) +func (x *S3OSInfo) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *S3OSInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_S3OSInfo.Marshal(b, m, deterministic) -} -func (m *S3OSInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_S3OSInfo.Merge(m, src) -} -func (m *S3OSInfo) XXX_Size() int { - return xxx_messageInfo_S3OSInfo.Size(m) -} -func (m *S3OSInfo) XXX_DiscardUnknown() { - xxx_messageInfo_S3OSInfo.DiscardUnknown(m) + +func (*S3OSInfo) ProtoMessage() {} + +func (x *S3OSInfo) ProtoReflect() protoreflect.Message { + mi := &file_net_lp_rpc_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_S3OSInfo proto.InternalMessageInfo +// Deprecated: Use S3OSInfo.ProtoReflect.Descriptor instead. +func (*S3OSInfo) Descriptor() ([]byte, []int) { + return file_net_lp_rpc_proto_rawDescGZIP(), []int{5} +} -func (m *S3OSInfo) GetHost() string { - if m != nil { - return m.Host +func (x *S3OSInfo) GetHost() string { + if x != nil { + return x.Host } return "" } -func (m *S3OSInfo) GetKey() string { - if m != nil { - return m.Key +func (x *S3OSInfo) GetKey() string { + if x != nil { + return x.Key } return "" } -func (m *S3OSInfo) GetPolicy() string { - if m != nil { - return m.Policy +func (x *S3OSInfo) GetPolicy() string { + if x != nil { + return x.Policy } return "" } -func (m *S3OSInfo) GetSignature() string { - if m != nil { - return m.Signature +func (x *S3OSInfo) GetSignature() string { + if x != nil { + return x.Signature } return "" } -func (m *S3OSInfo) GetCredential() string { - if m != nil { - return m.Credential +func (x *S3OSInfo) GetCredential() string { + if x != nil { + return x.Credential } return "" } -func (m *S3OSInfo) GetXAmzDate() string { - if m != nil { - return m.XAmzDate +func (x *S3OSInfo) GetXAmzDate() string { + if x != nil { + return x.XAmzDate } return "" } // PriceInfo conveys pricing info for transcoding services type PriceInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // price in wei PricePerUnit int64 `protobuf:"varint,1,opt,name=pricePerUnit,proto3" json:"pricePerUnit,omitempty"` // Pixels covered in the price // Set price to 1 wei and pixelsPerUnit > 1 to have a smaller price granularity per pixel than 1 wei - PixelsPerUnit int64 `protobuf:"varint,2,opt,name=pixelsPerUnit,proto3" json:"pixelsPerUnit,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + PixelsPerUnit int64 `protobuf:"varint,2,opt,name=pixelsPerUnit,proto3" json:"pixelsPerUnit,omitempty"` } -func (m *PriceInfo) Reset() { *m = PriceInfo{} } -func (m *PriceInfo) String() string { return proto.CompactTextString(m) } -func (*PriceInfo) ProtoMessage() {} -func (*PriceInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{6} +func (x *PriceInfo) Reset() { + *x = PriceInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_net_lp_rpc_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *PriceInfo) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PriceInfo.Unmarshal(m, b) -} -func (m *PriceInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PriceInfo.Marshal(b, m, deterministic) -} -func (m *PriceInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_PriceInfo.Merge(m, src) -} -func (m *PriceInfo) XXX_Size() int { - return xxx_messageInfo_PriceInfo.Size(m) +func (x *PriceInfo) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *PriceInfo) XXX_DiscardUnknown() { - xxx_messageInfo_PriceInfo.DiscardUnknown(m) + +func (*PriceInfo) ProtoMessage() {} + +func (x *PriceInfo) ProtoReflect() protoreflect.Message { + mi := &file_net_lp_rpc_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_PriceInfo proto.InternalMessageInfo +// Deprecated: Use PriceInfo.ProtoReflect.Descriptor instead. +func (*PriceInfo) Descriptor() ([]byte, []int) { + return file_net_lp_rpc_proto_rawDescGZIP(), []int{6} +} -func (m *PriceInfo) GetPricePerUnit() int64 { - if m != nil { - return m.PricePerUnit +func (x *PriceInfo) GetPricePerUnit() int64 { + if x != nil { + return x.PricePerUnit } return 0 } -func (m *PriceInfo) GetPixelsPerUnit() int64 { - if m != nil { - return m.PixelsPerUnit +func (x *PriceInfo) GetPixelsPerUnit() int64 { + if x != nil { + return x.PixelsPerUnit } return 0 } type Capabilities struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Bit string of supported features - one bit per feature Bitstring []uint64 `protobuf:"varint,1,rep,packed,name=bitstring,proto3" json:"bitstring,omitempty"` // Bit string of features that are required to be supported Mandatories []uint64 `protobuf:"varint,2,rep,packed,name=mandatories,proto3" json:"mandatories,omitempty"` // Capacity corresponding to each capability - Capacities map[uint32]uint32 `protobuf:"bytes,3,rep,name=capacities,proto3" json:"capacities,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` - Version string `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` - Constraints *Capabilities_Constraints `protobuf:"bytes,5,opt,name=constraints,proto3" json:"constraints,omitempty"` - CapabilityConstraints map[uint32]*Capabilities_CapabilityConstraints `protobuf:"bytes,6,rep,name=capabilityConstraints,proto3" json:"capabilityConstraints,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Capabilities) Reset() { *m = Capabilities{} } -func (m *Capabilities) String() string { return proto.CompactTextString(m) } -func (*Capabilities) ProtoMessage() {} -func (*Capabilities) Descriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{7} -} - -func (m *Capabilities) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Capabilities.Unmarshal(m, b) -} -func (m *Capabilities) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Capabilities.Marshal(b, m, deterministic) + Capacities map[uint32]uint32 `protobuf:"bytes,3,rep,name=capacities,proto3" json:"capacities,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` + Constraints map[uint32]*Capabilities_Constraints `protobuf:"bytes,4,rep,name=constraints,proto3" json:"constraints,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } -func (m *Capabilities) XXX_Merge(src proto.Message) { - xxx_messageInfo_Capabilities.Merge(m, src) -} -func (m *Capabilities) XXX_Size() int { - return xxx_messageInfo_Capabilities.Size(m) -} -func (m *Capabilities) XXX_DiscardUnknown() { - xxx_messageInfo_Capabilities.DiscardUnknown(m) -} - -var xxx_messageInfo_Capabilities proto.InternalMessageInfo -func (m *Capabilities) GetBitstring() []uint64 { - if m != nil { - return m.Bitstring +func (x *Capabilities) Reset() { + *x = Capabilities{} + if protoimpl.UnsafeEnabled { + mi := &file_net_lp_rpc_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (m *Capabilities) GetMandatories() []uint64 { - if m != nil { - return m.Mandatories - } - return nil +func (x *Capabilities) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Capabilities) GetCapacities() map[uint32]uint32 { - if m != nil { - return m.Capacities - } - return nil -} +func (*Capabilities) ProtoMessage() {} -func (m *Capabilities) GetVersion() string { - if m != nil { - return m.Version +func (x *Capabilities) ProtoReflect() protoreflect.Message { + mi := &file_net_lp_rpc_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return "" + return mi.MessageOf(x) } -func (m *Capabilities) GetConstraints() *Capabilities_Constraints { - if m != nil { - return m.Constraints - } - return nil +// Deprecated: Use Capabilities.ProtoReflect.Descriptor instead. +func (*Capabilities) Descriptor() ([]byte, []int) { + return file_net_lp_rpc_proto_rawDescGZIP(), []int{7} } -func (m *Capabilities) GetCapabilityConstraints() map[uint32]*Capabilities_CapabilityConstraints { - if m != nil { - return m.CapabilityConstraints +func (x *Capabilities) GetBitstring() []uint64 { + if x != nil { + return x.Bitstring } return nil } -// Non-binary general constraints. -type Capabilities_Constraints struct { - MinVersion string `protobuf:"bytes,1,opt,name=minVersion,proto3" json:"minVersion,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Capabilities_Constraints) Reset() { *m = Capabilities_Constraints{} } -func (m *Capabilities_Constraints) String() string { return proto.CompactTextString(m) } -func (*Capabilities_Constraints) ProtoMessage() {} -func (*Capabilities_Constraints) Descriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{7, 1} -} - -func (m *Capabilities_Constraints) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Capabilities_Constraints.Unmarshal(m, b) -} -func (m *Capabilities_Constraints) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Capabilities_Constraints.Marshal(b, m, deterministic) -} -func (m *Capabilities_Constraints) XXX_Merge(src proto.Message) { - xxx_messageInfo_Capabilities_Constraints.Merge(m, src) -} -func (m *Capabilities_Constraints) XXX_Size() int { - return xxx_messageInfo_Capabilities_Constraints.Size(m) -} -func (m *Capabilities_Constraints) XXX_DiscardUnknown() { - xxx_messageInfo_Capabilities_Constraints.DiscardUnknown(m) -} - -var xxx_messageInfo_Capabilities_Constraints proto.InternalMessageInfo - -func (m *Capabilities_Constraints) GetMinVersion() string { - if m != nil { - return m.MinVersion +func (x *Capabilities) GetMandatories() []uint64 { + if x != nil { + return x.Mandatories } - return "" -} - -// Non-binary capability constraints, such as supported ranges. -type Capabilities_CapabilityConstraints struct { - Models map[string]*Capabilities_CapabilityConstraints_ModelConstraint `protobuf:"bytes,1,rep,name=models,proto3" json:"models,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Capabilities_CapabilityConstraints) Reset() { *m = Capabilities_CapabilityConstraints{} } -func (m *Capabilities_CapabilityConstraints) String() string { return proto.CompactTextString(m) } -func (*Capabilities_CapabilityConstraints) ProtoMessage() {} -func (*Capabilities_CapabilityConstraints) Descriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{7, 2} -} - -func (m *Capabilities_CapabilityConstraints) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Capabilities_CapabilityConstraints.Unmarshal(m, b) -} -func (m *Capabilities_CapabilityConstraints) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Capabilities_CapabilityConstraints.Marshal(b, m, deterministic) -} -func (m *Capabilities_CapabilityConstraints) XXX_Merge(src proto.Message) { - xxx_messageInfo_Capabilities_CapabilityConstraints.Merge(m, src) -} -func (m *Capabilities_CapabilityConstraints) XXX_Size() int { - return xxx_messageInfo_Capabilities_CapabilityConstraints.Size(m) -} -func (m *Capabilities_CapabilityConstraints) XXX_DiscardUnknown() { - xxx_messageInfo_Capabilities_CapabilityConstraints.DiscardUnknown(m) + return nil } -var xxx_messageInfo_Capabilities_CapabilityConstraints proto.InternalMessageInfo - -func (m *Capabilities_CapabilityConstraints) GetModels() map[string]*Capabilities_CapabilityConstraints_ModelConstraint { - if m != nil { - return m.Models +func (x *Capabilities) GetCapacities() map[uint32]uint32 { + if x != nil { + return x.Capacities } return nil } -type Capabilities_CapabilityConstraints_ModelConstraint struct { - Warm bool `protobuf:"varint,1,opt,name=warm,proto3" json:"warm,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Capabilities_CapabilityConstraints_ModelConstraint) Reset() { - *m = Capabilities_CapabilityConstraints_ModelConstraint{} -} -func (m *Capabilities_CapabilityConstraints_ModelConstraint) String() string { - return proto.CompactTextString(m) -} -func (*Capabilities_CapabilityConstraints_ModelConstraint) ProtoMessage() {} -func (*Capabilities_CapabilityConstraints_ModelConstraint) Descriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{7, 2, 0} -} - -func (m *Capabilities_CapabilityConstraints_ModelConstraint) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Capabilities_CapabilityConstraints_ModelConstraint.Unmarshal(m, b) -} -func (m *Capabilities_CapabilityConstraints_ModelConstraint) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Capabilities_CapabilityConstraints_ModelConstraint.Marshal(b, m, deterministic) -} -func (m *Capabilities_CapabilityConstraints_ModelConstraint) XXX_Merge(src proto.Message) { - xxx_messageInfo_Capabilities_CapabilityConstraints_ModelConstraint.Merge(m, src) -} -func (m *Capabilities_CapabilityConstraints_ModelConstraint) XXX_Size() int { - return xxx_messageInfo_Capabilities_CapabilityConstraints_ModelConstraint.Size(m) -} -func (m *Capabilities_CapabilityConstraints_ModelConstraint) XXX_DiscardUnknown() { - xxx_messageInfo_Capabilities_CapabilityConstraints_ModelConstraint.DiscardUnknown(m) -} - -var xxx_messageInfo_Capabilities_CapabilityConstraints_ModelConstraint proto.InternalMessageInfo - -func (m *Capabilities_CapabilityConstraints_ModelConstraint) GetWarm() bool { - if m != nil { - return m.Warm +func (x *Capabilities) GetConstraints() map[uint32]*Capabilities_Constraints { + if x != nil { + return x.Constraints } - return false + return nil } // The orchestrator sends this in response to `GetOrchestrator`, containing // miscellaneous data related to the job. type OrchestratorInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // URI of the transcoder to use for submitting segments. Transcoder string `protobuf:"bytes,1,opt,name=transcoder,proto3" json:"transcoder,omitempty"` // Parameters for probabilistic micropayment tickets @@ -746,148 +779,164 @@ type OrchestratorInfo struct { // Data for transcoding authentication AuthToken *AuthToken `protobuf:"bytes,6,opt,name=auth_token,json=authToken,proto3" json:"auth_token,omitempty"` // Orchestrator returns info about own input object storage, if it wants it to be used. - Storage []*OSInfo `protobuf:"bytes,32,rep,name=storage,proto3" json:"storage,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Storage []*OSInfo `protobuf:"bytes,32,rep,name=storage,proto3" json:"storage,omitempty"` } -func (m *OrchestratorInfo) Reset() { *m = OrchestratorInfo{} } -func (m *OrchestratorInfo) String() string { return proto.CompactTextString(m) } -func (*OrchestratorInfo) ProtoMessage() {} -func (*OrchestratorInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{8} +func (x *OrchestratorInfo) Reset() { + *x = OrchestratorInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_net_lp_rpc_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *OrchestratorInfo) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_OrchestratorInfo.Unmarshal(m, b) -} -func (m *OrchestratorInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_OrchestratorInfo.Marshal(b, m, deterministic) -} -func (m *OrchestratorInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_OrchestratorInfo.Merge(m, src) -} -func (m *OrchestratorInfo) XXX_Size() int { - return xxx_messageInfo_OrchestratorInfo.Size(m) +func (x *OrchestratorInfo) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *OrchestratorInfo) XXX_DiscardUnknown() { - xxx_messageInfo_OrchestratorInfo.DiscardUnknown(m) + +func (*OrchestratorInfo) ProtoMessage() {} + +func (x *OrchestratorInfo) ProtoReflect() protoreflect.Message { + mi := &file_net_lp_rpc_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_OrchestratorInfo proto.InternalMessageInfo +// Deprecated: Use OrchestratorInfo.ProtoReflect.Descriptor instead. +func (*OrchestratorInfo) Descriptor() ([]byte, []int) { + return file_net_lp_rpc_proto_rawDescGZIP(), []int{8} +} -func (m *OrchestratorInfo) GetTranscoder() string { - if m != nil { - return m.Transcoder +func (x *OrchestratorInfo) GetTranscoder() string { + if x != nil { + return x.Transcoder } return "" } -func (m *OrchestratorInfo) GetTicketParams() *TicketParams { - if m != nil { - return m.TicketParams +func (x *OrchestratorInfo) GetTicketParams() *TicketParams { + if x != nil { + return x.TicketParams } return nil } -func (m *OrchestratorInfo) GetPriceInfo() *PriceInfo { - if m != nil { - return m.PriceInfo +func (x *OrchestratorInfo) GetPriceInfo() *PriceInfo { + if x != nil { + return x.PriceInfo } return nil } -func (m *OrchestratorInfo) GetAddress() []byte { - if m != nil { - return m.Address +func (x *OrchestratorInfo) GetAddress() []byte { + if x != nil { + return x.Address } return nil } -func (m *OrchestratorInfo) GetCapabilities() *Capabilities { - if m != nil { - return m.Capabilities +func (x *OrchestratorInfo) GetCapabilities() *Capabilities { + if x != nil { + return x.Capabilities } return nil } -func (m *OrchestratorInfo) GetAuthToken() *AuthToken { - if m != nil { - return m.AuthToken +func (x *OrchestratorInfo) GetAuthToken() *AuthToken { + if x != nil { + return x.AuthToken } return nil } -func (m *OrchestratorInfo) GetStorage() []*OSInfo { - if m != nil { - return m.Storage +func (x *OrchestratorInfo) GetStorage() []*OSInfo { + if x != nil { + return x.Storage } return nil } // Data for transcoding authentication that is included in the OrchestratorInfo message during discovery type AuthToken struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Record used to authenticate for a transcode session // Opaque to the receiver Token []byte `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` // ID of the transcode session that the token is authenticating for SessionId string `protobuf:"bytes,2,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` // Timestamp when the token expires - Expiration int64 `protobuf:"varint,3,opt,name=expiration,proto3" json:"expiration,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Expiration int64 `protobuf:"varint,3,opt,name=expiration,proto3" json:"expiration,omitempty"` } -func (m *AuthToken) Reset() { *m = AuthToken{} } -func (m *AuthToken) String() string { return proto.CompactTextString(m) } -func (*AuthToken) ProtoMessage() {} -func (*AuthToken) Descriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{9} +func (x *AuthToken) Reset() { + *x = AuthToken{} + if protoimpl.UnsafeEnabled { + mi := &file_net_lp_rpc_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *AuthToken) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_AuthToken.Unmarshal(m, b) -} -func (m *AuthToken) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_AuthToken.Marshal(b, m, deterministic) +func (x *AuthToken) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *AuthToken) XXX_Merge(src proto.Message) { - xxx_messageInfo_AuthToken.Merge(m, src) -} -func (m *AuthToken) XXX_Size() int { - return xxx_messageInfo_AuthToken.Size(m) -} -func (m *AuthToken) XXX_DiscardUnknown() { - xxx_messageInfo_AuthToken.DiscardUnknown(m) + +func (*AuthToken) ProtoMessage() {} + +func (x *AuthToken) ProtoReflect() protoreflect.Message { + mi := &file_net_lp_rpc_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_AuthToken proto.InternalMessageInfo +// Deprecated: Use AuthToken.ProtoReflect.Descriptor instead. +func (*AuthToken) Descriptor() ([]byte, []int) { + return file_net_lp_rpc_proto_rawDescGZIP(), []int{9} +} -func (m *AuthToken) GetToken() []byte { - if m != nil { - return m.Token +func (x *AuthToken) GetToken() []byte { + if x != nil { + return x.Token } return nil } -func (m *AuthToken) GetSessionId() string { - if m != nil { - return m.SessionId +func (x *AuthToken) GetSessionId() string { + if x != nil { + return x.SessionId } return "" } -func (m *AuthToken) GetExpiration() int64 { - if m != nil { - return m.Expiration +func (x *AuthToken) GetExpiration() int64 { + if x != nil { + return x.Expiration } return 0 } // Data included by the broadcaster when submitting a segment for transcoding. type SegData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Manifest ID this segment belongs to ManifestId []byte `protobuf:"bytes,1,opt,name=manifestId,proto3" json:"manifestId,omitempty"` // Sequence number of the segment to be transcoded @@ -921,194 +970,210 @@ type SegData struct { // Transcoding parameters specific to this segment SegmentParameters *SegParameters `protobuf:"bytes,37,opt,name=segment_parameters,json=segmentParameters,proto3" json:"segment_parameters,omitempty"` // Force HW Session Reinit - ForceSessionReinit bool `protobuf:"varint,38,opt,name=ForceSessionReinit,proto3" json:"ForceSessionReinit,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + ForceSessionReinit bool `protobuf:"varint,38,opt,name=ForceSessionReinit,proto3" json:"ForceSessionReinit,omitempty"` } -func (m *SegData) Reset() { *m = SegData{} } -func (m *SegData) String() string { return proto.CompactTextString(m) } -func (*SegData) ProtoMessage() {} -func (*SegData) Descriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{10} +func (x *SegData) Reset() { + *x = SegData{} + if protoimpl.UnsafeEnabled { + mi := &file_net_lp_rpc_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *SegData) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_SegData.Unmarshal(m, b) -} -func (m *SegData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_SegData.Marshal(b, m, deterministic) -} -func (m *SegData) XXX_Merge(src proto.Message) { - xxx_messageInfo_SegData.Merge(m, src) -} -func (m *SegData) XXX_Size() int { - return xxx_messageInfo_SegData.Size(m) +func (x *SegData) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *SegData) XXX_DiscardUnknown() { - xxx_messageInfo_SegData.DiscardUnknown(m) + +func (*SegData) ProtoMessage() {} + +func (x *SegData) ProtoReflect() protoreflect.Message { + mi := &file_net_lp_rpc_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_SegData proto.InternalMessageInfo +// Deprecated: Use SegData.ProtoReflect.Descriptor instead. +func (*SegData) Descriptor() ([]byte, []int) { + return file_net_lp_rpc_proto_rawDescGZIP(), []int{10} +} -func (m *SegData) GetManifestId() []byte { - if m != nil { - return m.ManifestId +func (x *SegData) GetManifestId() []byte { + if x != nil { + return x.ManifestId } return nil } -func (m *SegData) GetSeq() int64 { - if m != nil { - return m.Seq +func (x *SegData) GetSeq() int64 { + if x != nil { + return x.Seq } return 0 } -func (m *SegData) GetHash() []byte { - if m != nil { - return m.Hash +func (x *SegData) GetHash() []byte { + if x != nil { + return x.Hash } return nil } -func (m *SegData) GetProfiles() []byte { - if m != nil { - return m.Profiles +func (x *SegData) GetProfiles() []byte { + if x != nil { + return x.Profiles } return nil } -func (m *SegData) GetSig() []byte { - if m != nil { - return m.Sig +func (x *SegData) GetSig() []byte { + if x != nil { + return x.Sig } return nil } -func (m *SegData) GetDuration() int32 { - if m != nil { - return m.Duration +func (x *SegData) GetDuration() int32 { + if x != nil { + return x.Duration } return 0 } -func (m *SegData) GetCapabilities() *Capabilities { - if m != nil { - return m.Capabilities +func (x *SegData) GetCapabilities() *Capabilities { + if x != nil { + return x.Capabilities } return nil } -func (m *SegData) GetAuthToken() *AuthToken { - if m != nil { - return m.AuthToken +func (x *SegData) GetAuthToken() *AuthToken { + if x != nil { + return x.AuthToken } return nil } -func (m *SegData) GetCalcPerceptualHash() bool { - if m != nil { - return m.CalcPerceptualHash +func (x *SegData) GetCalcPerceptualHash() bool { + if x != nil { + return x.CalcPerceptualHash } return false } -func (m *SegData) GetStorage() []*OSInfo { - if m != nil { - return m.Storage +func (x *SegData) GetStorage() []*OSInfo { + if x != nil { + return x.Storage } return nil } -func (m *SegData) GetFullProfiles() []*VideoProfile { - if m != nil { - return m.FullProfiles +func (x *SegData) GetFullProfiles() []*VideoProfile { + if x != nil { + return x.FullProfiles } return nil } -func (m *SegData) GetFullProfiles2() []*VideoProfile { - if m != nil { - return m.FullProfiles2 +func (x *SegData) GetFullProfiles2() []*VideoProfile { + if x != nil { + return x.FullProfiles2 } return nil } -func (m *SegData) GetFullProfiles3() []*VideoProfile { - if m != nil { - return m.FullProfiles3 +func (x *SegData) GetFullProfiles3() []*VideoProfile { + if x != nil { + return x.FullProfiles3 } return nil } -func (m *SegData) GetSegmentParameters() *SegParameters { - if m != nil { - return m.SegmentParameters +func (x *SegData) GetSegmentParameters() *SegParameters { + if x != nil { + return x.SegmentParameters } return nil } -func (m *SegData) GetForceSessionReinit() bool { - if m != nil { - return m.ForceSessionReinit +func (x *SegData) GetForceSessionReinit() bool { + if x != nil { + return x.ForceSessionReinit } return false } type SegParameters struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Start timestamp from which to start encoding // Milliseconds, from start of the file From uint64 `protobuf:"varint,1,opt,name=from,proto3" json:"from,omitempty"` // Skip all frames after that timestamp // Milliseconds, from start of the file - To uint64 `protobuf:"varint,2,opt,name=to,proto3" json:"to,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + To uint64 `protobuf:"varint,2,opt,name=to,proto3" json:"to,omitempty"` } -func (m *SegParameters) Reset() { *m = SegParameters{} } -func (m *SegParameters) String() string { return proto.CompactTextString(m) } -func (*SegParameters) ProtoMessage() {} -func (*SegParameters) Descriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{11} +func (x *SegParameters) Reset() { + *x = SegParameters{} + if protoimpl.UnsafeEnabled { + mi := &file_net_lp_rpc_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *SegParameters) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_SegParameters.Unmarshal(m, b) -} -func (m *SegParameters) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_SegParameters.Marshal(b, m, deterministic) +func (x *SegParameters) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *SegParameters) XXX_Merge(src proto.Message) { - xxx_messageInfo_SegParameters.Merge(m, src) -} -func (m *SegParameters) XXX_Size() int { - return xxx_messageInfo_SegParameters.Size(m) -} -func (m *SegParameters) XXX_DiscardUnknown() { - xxx_messageInfo_SegParameters.DiscardUnknown(m) + +func (*SegParameters) ProtoMessage() {} + +func (x *SegParameters) ProtoReflect() protoreflect.Message { + mi := &file_net_lp_rpc_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_SegParameters proto.InternalMessageInfo +// Deprecated: Use SegParameters.ProtoReflect.Descriptor instead. +func (*SegParameters) Descriptor() ([]byte, []int) { + return file_net_lp_rpc_proto_rawDescGZIP(), []int{11} +} -func (m *SegParameters) GetFrom() uint64 { - if m != nil { - return m.From +func (x *SegParameters) GetFrom() uint64 { + if x != nil { + return x.From } return 0 } -func (m *SegParameters) GetTo() uint64 { - if m != nil { - return m.To +func (x *SegParameters) GetTo() uint64 { + if x != nil { + return x.To } return 0 } type VideoProfile struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Name of VideoProfile Name string `protobuf:"bytes,16,opt,name=name,proto3" json:"name,omitempty"` // Width of VideoProfile @@ -1127,306 +1192,318 @@ type VideoProfile struct { // GOP interval Gop int32 `protobuf:"varint,24,opt,name=gop,proto3" json:"gop,omitempty"` // Encoder (video codec) - Encoder VideoProfile_VideoCodec `protobuf:"varint,25,opt,name=encoder,proto3,enum=net.VideoProfile_VideoCodec" json:"encoder,omitempty"` - ColorDepth int32 `protobuf:"varint,26,opt,name=colorDepth,proto3" json:"colorDepth,omitempty"` - ChromaFormat VideoProfile_ChromaSubsampling `protobuf:"varint,27,opt,name=chromaFormat,proto3,enum=net.VideoProfile_ChromaSubsampling" json:"chromaFormat,omitempty"` - Quality uint32 `protobuf:"varint,28,opt,name=quality,proto3" json:"quality,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *VideoProfile) Reset() { *m = VideoProfile{} } -func (m *VideoProfile) String() string { return proto.CompactTextString(m) } -func (*VideoProfile) ProtoMessage() {} -func (*VideoProfile) Descriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{12} + Encoder VideoProfile_VideoCodec `protobuf:"varint,25,opt,name=encoder,proto3,enum=net.VideoProfile_VideoCodec" json:"encoder,omitempty"` + ColorDepth int32 `protobuf:"varint,26,opt,name=colorDepth,proto3" json:"colorDepth,omitempty"` + ChromaFormat VideoProfile_ChromaSubsampling `protobuf:"varint,27,opt,name=chromaFormat,proto3,enum=net.VideoProfile_ChromaSubsampling" json:"chromaFormat,omitempty"` + Quality uint32 `protobuf:"varint,28,opt,name=quality,proto3" json:"quality,omitempty"` } -func (m *VideoProfile) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_VideoProfile.Unmarshal(m, b) -} -func (m *VideoProfile) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_VideoProfile.Marshal(b, m, deterministic) -} -func (m *VideoProfile) XXX_Merge(src proto.Message) { - xxx_messageInfo_VideoProfile.Merge(m, src) +func (x *VideoProfile) Reset() { + *x = VideoProfile{} + if protoimpl.UnsafeEnabled { + mi := &file_net_lp_rpc_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *VideoProfile) XXX_Size() int { - return xxx_messageInfo_VideoProfile.Size(m) + +func (x *VideoProfile) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *VideoProfile) XXX_DiscardUnknown() { - xxx_messageInfo_VideoProfile.DiscardUnknown(m) + +func (*VideoProfile) ProtoMessage() {} + +func (x *VideoProfile) ProtoReflect() protoreflect.Message { + mi := &file_net_lp_rpc_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_VideoProfile proto.InternalMessageInfo +// Deprecated: Use VideoProfile.ProtoReflect.Descriptor instead. +func (*VideoProfile) Descriptor() ([]byte, []int) { + return file_net_lp_rpc_proto_rawDescGZIP(), []int{12} +} -func (m *VideoProfile) GetName() string { - if m != nil { - return m.Name +func (x *VideoProfile) GetName() string { + if x != nil { + return x.Name } return "" } -func (m *VideoProfile) GetWidth() int32 { - if m != nil { - return m.Width +func (x *VideoProfile) GetWidth() int32 { + if x != nil { + return x.Width } return 0 } -func (m *VideoProfile) GetHeight() int32 { - if m != nil { - return m.Height +func (x *VideoProfile) GetHeight() int32 { + if x != nil { + return x.Height } return 0 } -func (m *VideoProfile) GetBitrate() int32 { - if m != nil { - return m.Bitrate +func (x *VideoProfile) GetBitrate() int32 { + if x != nil { + return x.Bitrate } return 0 } -func (m *VideoProfile) GetFps() uint32 { - if m != nil { - return m.Fps +func (x *VideoProfile) GetFps() uint32 { + if x != nil { + return x.Fps } return 0 } -func (m *VideoProfile) GetFormat() VideoProfile_Format { - if m != nil { - return m.Format +func (x *VideoProfile) GetFormat() VideoProfile_Format { + if x != nil { + return x.Format } return VideoProfile_MPEGTS } -func (m *VideoProfile) GetFpsDen() uint32 { - if m != nil { - return m.FpsDen +func (x *VideoProfile) GetFpsDen() uint32 { + if x != nil { + return x.FpsDen } return 0 } -func (m *VideoProfile) GetProfile() VideoProfile_Profile { - if m != nil { - return m.Profile +func (x *VideoProfile) GetProfile() VideoProfile_Profile { + if x != nil { + return x.Profile } return VideoProfile_ENCODER_DEFAULT } -func (m *VideoProfile) GetGop() int32 { - if m != nil { - return m.Gop +func (x *VideoProfile) GetGop() int32 { + if x != nil { + return x.Gop } return 0 } -func (m *VideoProfile) GetEncoder() VideoProfile_VideoCodec { - if m != nil { - return m.Encoder +func (x *VideoProfile) GetEncoder() VideoProfile_VideoCodec { + if x != nil { + return x.Encoder } return VideoProfile_H264 } -func (m *VideoProfile) GetColorDepth() int32 { - if m != nil { - return m.ColorDepth +func (x *VideoProfile) GetColorDepth() int32 { + if x != nil { + return x.ColorDepth } return 0 } -func (m *VideoProfile) GetChromaFormat() VideoProfile_ChromaSubsampling { - if m != nil { - return m.ChromaFormat +func (x *VideoProfile) GetChromaFormat() VideoProfile_ChromaSubsampling { + if x != nil { + return x.ChromaFormat } return VideoProfile_CHROMA_420 } -func (m *VideoProfile) GetQuality() uint32 { - if m != nil { - return m.Quality +func (x *VideoProfile) GetQuality() uint32 { + if x != nil { + return x.Quality } return 0 } // Individual transcoded segment data. type TranscodedSegmentData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // URL where the transcoded data can be downloaded from. Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` // Amount of pixels processed (output pixels) Pixels int64 `protobuf:"varint,2,opt,name=pixels,proto3" json:"pixels,omitempty"` // URL where the perceptual hash data can be downloaded from (can be empty) - PerceptualHashUrl string `protobuf:"bytes,3,opt,name=perceptual_hash_url,json=perceptualHashUrl,proto3" json:"perceptual_hash_url,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + PerceptualHashUrl string `protobuf:"bytes,3,opt,name=perceptual_hash_url,json=perceptualHashUrl,proto3" json:"perceptual_hash_url,omitempty"` } -func (m *TranscodedSegmentData) Reset() { *m = TranscodedSegmentData{} } -func (m *TranscodedSegmentData) String() string { return proto.CompactTextString(m) } -func (*TranscodedSegmentData) ProtoMessage() {} -func (*TranscodedSegmentData) Descriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{13} +func (x *TranscodedSegmentData) Reset() { + *x = TranscodedSegmentData{} + if protoimpl.UnsafeEnabled { + mi := &file_net_lp_rpc_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *TranscodedSegmentData) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_TranscodedSegmentData.Unmarshal(m, b) +func (x *TranscodedSegmentData) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *TranscodedSegmentData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_TranscodedSegmentData.Marshal(b, m, deterministic) -} -func (m *TranscodedSegmentData) XXX_Merge(src proto.Message) { - xxx_messageInfo_TranscodedSegmentData.Merge(m, src) -} -func (m *TranscodedSegmentData) XXX_Size() int { - return xxx_messageInfo_TranscodedSegmentData.Size(m) -} -func (m *TranscodedSegmentData) XXX_DiscardUnknown() { - xxx_messageInfo_TranscodedSegmentData.DiscardUnknown(m) + +func (*TranscodedSegmentData) ProtoMessage() {} + +func (x *TranscodedSegmentData) ProtoReflect() protoreflect.Message { + mi := &file_net_lp_rpc_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_TranscodedSegmentData proto.InternalMessageInfo +// Deprecated: Use TranscodedSegmentData.ProtoReflect.Descriptor instead. +func (*TranscodedSegmentData) Descriptor() ([]byte, []int) { + return file_net_lp_rpc_proto_rawDescGZIP(), []int{13} +} -func (m *TranscodedSegmentData) GetUrl() string { - if m != nil { - return m.Url +func (x *TranscodedSegmentData) GetUrl() string { + if x != nil { + return x.Url } return "" } -func (m *TranscodedSegmentData) GetPixels() int64 { - if m != nil { - return m.Pixels +func (x *TranscodedSegmentData) GetPixels() int64 { + if x != nil { + return x.Pixels } return 0 } -func (m *TranscodedSegmentData) GetPerceptualHashUrl() string { - if m != nil { - return m.PerceptualHashUrl +func (x *TranscodedSegmentData) GetPerceptualHashUrl() string { + if x != nil { + return x.PerceptualHashUrl } return "" } // A set of transcoded segments following the profiles specified in the job. type TranscodeData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Transcoded data, in the order specified in the job options Segments []*TranscodedSegmentData `protobuf:"bytes,1,rep,name=segments,proto3" json:"segments,omitempty"` // Signature of the hash of the concatenated hashes - Sig []byte `protobuf:"bytes,2,opt,name=sig,proto3" json:"sig,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Sig []byte `protobuf:"bytes,2,opt,name=sig,proto3" json:"sig,omitempty"` } -func (m *TranscodeData) Reset() { *m = TranscodeData{} } -func (m *TranscodeData) String() string { return proto.CompactTextString(m) } -func (*TranscodeData) ProtoMessage() {} -func (*TranscodeData) Descriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{14} +func (x *TranscodeData) Reset() { + *x = TranscodeData{} + if protoimpl.UnsafeEnabled { + mi := &file_net_lp_rpc_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *TranscodeData) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_TranscodeData.Unmarshal(m, b) -} -func (m *TranscodeData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_TranscodeData.Marshal(b, m, deterministic) +func (x *TranscodeData) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *TranscodeData) XXX_Merge(src proto.Message) { - xxx_messageInfo_TranscodeData.Merge(m, src) -} -func (m *TranscodeData) XXX_Size() int { - return xxx_messageInfo_TranscodeData.Size(m) -} -func (m *TranscodeData) XXX_DiscardUnknown() { - xxx_messageInfo_TranscodeData.DiscardUnknown(m) + +func (*TranscodeData) ProtoMessage() {} + +func (x *TranscodeData) ProtoReflect() protoreflect.Message { + mi := &file_net_lp_rpc_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_TranscodeData proto.InternalMessageInfo +// Deprecated: Use TranscodeData.ProtoReflect.Descriptor instead. +func (*TranscodeData) Descriptor() ([]byte, []int) { + return file_net_lp_rpc_proto_rawDescGZIP(), []int{14} +} -func (m *TranscodeData) GetSegments() []*TranscodedSegmentData { - if m != nil { - return m.Segments +func (x *TranscodeData) GetSegments() []*TranscodedSegmentData { + if x != nil { + return x.Segments } return nil } -func (m *TranscodeData) GetSig() []byte { - if m != nil { - return m.Sig +func (x *TranscodeData) GetSig() []byte { + if x != nil { + return x.Sig } return nil } // Response that a transcoder sends after transcoding a segment. type TranscodeResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Sequence number of the transcoded results. Seq int64 `protobuf:"varint,1,opt,name=seq,proto3" json:"seq,omitempty"` // Result of transcoding can be an error, or successful with more info // - // Types that are valid to be assigned to Result: + // Types that are assignable to Result: // // *TranscodeResult_Error // *TranscodeResult_Data Result isTranscodeResult_Result `protobuf_oneof:"result"` // Used to notify a broadcaster of updated orchestrator information - Info *OrchestratorInfo `protobuf:"bytes,16,opt,name=info,proto3" json:"info,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Info *OrchestratorInfo `protobuf:"bytes,16,opt,name=info,proto3" json:"info,omitempty"` } -func (m *TranscodeResult) Reset() { *m = TranscodeResult{} } -func (m *TranscodeResult) String() string { return proto.CompactTextString(m) } -func (*TranscodeResult) ProtoMessage() {} -func (*TranscodeResult) Descriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{15} +func (x *TranscodeResult) Reset() { + *x = TranscodeResult{} + if protoimpl.UnsafeEnabled { + mi := &file_net_lp_rpc_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *TranscodeResult) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_TranscodeResult.Unmarshal(m, b) -} -func (m *TranscodeResult) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_TranscodeResult.Marshal(b, m, deterministic) -} -func (m *TranscodeResult) XXX_Merge(src proto.Message) { - xxx_messageInfo_TranscodeResult.Merge(m, src) -} -func (m *TranscodeResult) XXX_Size() int { - return xxx_messageInfo_TranscodeResult.Size(m) -} -func (m *TranscodeResult) XXX_DiscardUnknown() { - xxx_messageInfo_TranscodeResult.DiscardUnknown(m) +func (x *TranscodeResult) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_TranscodeResult proto.InternalMessageInfo +func (*TranscodeResult) ProtoMessage() {} -func (m *TranscodeResult) GetSeq() int64 { - if m != nil { - return m.Seq +func (x *TranscodeResult) ProtoReflect() protoreflect.Message { + mi := &file_net_lp_rpc_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return 0 -} - -type isTranscodeResult_Result interface { - isTranscodeResult_Result() + return mi.MessageOf(x) } -type TranscodeResult_Error struct { - Error string `protobuf:"bytes,2,opt,name=error,proto3,oneof"` +// Deprecated: Use TranscodeResult.ProtoReflect.Descriptor instead. +func (*TranscodeResult) Descriptor() ([]byte, []int) { + return file_net_lp_rpc_proto_rawDescGZIP(), []int{15} } -type TranscodeResult_Data struct { - Data *TranscodeData `protobuf:"bytes,3,opt,name=data,proto3,oneof"` +func (x *TranscodeResult) GetSeq() int64 { + if x != nil { + return x.Seq + } + return 0 } -func (*TranscodeResult_Error) isTranscodeResult_Result() {} - -func (*TranscodeResult_Data) isTranscodeResult_Result() {} - func (m *TranscodeResult) GetResult() isTranscodeResult_Result { if m != nil { return m.Result @@ -1434,96 +1511,116 @@ func (m *TranscodeResult) GetResult() isTranscodeResult_Result { return nil } -func (m *TranscodeResult) GetError() string { - if x, ok := m.GetResult().(*TranscodeResult_Error); ok { +func (x *TranscodeResult) GetError() string { + if x, ok := x.GetResult().(*TranscodeResult_Error); ok { return x.Error } return "" } -func (m *TranscodeResult) GetData() *TranscodeData { - if x, ok := m.GetResult().(*TranscodeResult_Data); ok { +func (x *TranscodeResult) GetData() *TranscodeData { + if x, ok := x.GetResult().(*TranscodeResult_Data); ok { return x.Data } return nil } -func (m *TranscodeResult) GetInfo() *OrchestratorInfo { - if m != nil { - return m.Info +func (x *TranscodeResult) GetInfo() *OrchestratorInfo { + if x != nil { + return x.Info } return nil } -// XXX_OneofWrappers is for the internal use of the proto package. -func (*TranscodeResult) XXX_OneofWrappers() []interface{} { - return []interface{}{ - (*TranscodeResult_Error)(nil), - (*TranscodeResult_Data)(nil), - } +type isTranscodeResult_Result interface { + isTranscodeResult_Result() +} + +type TranscodeResult_Error struct { + Error string `protobuf:"bytes,2,opt,name=error,proto3,oneof"` +} + +type TranscodeResult_Data struct { + Data *TranscodeData `protobuf:"bytes,3,opt,name=data,proto3,oneof"` } +func (*TranscodeResult_Error) isTranscodeResult_Result() {} + +func (*TranscodeResult_Data) isTranscodeResult_Result() {} + // Sent by the transcoder to register itself to the orchestrator. type RegisterRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Shared secret for auth Secret string `protobuf:"bytes,1,opt,name=secret,proto3" json:"secret,omitempty"` // Transcoder capacity Capacity int64 `protobuf:"varint,2,opt,name=capacity,proto3" json:"capacity,omitempty"` // Transcoder capabilities - Capabilities *Capabilities `protobuf:"bytes,3,opt,name=capabilities,proto3" json:"capabilities,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Capabilities *Capabilities `protobuf:"bytes,3,opt,name=capabilities,proto3" json:"capabilities,omitempty"` } -func (m *RegisterRequest) Reset() { *m = RegisterRequest{} } -func (m *RegisterRequest) String() string { return proto.CompactTextString(m) } -func (*RegisterRequest) ProtoMessage() {} -func (*RegisterRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{16} +func (x *RegisterRequest) Reset() { + *x = RegisterRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_net_lp_rpc_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *RegisterRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_RegisterRequest.Unmarshal(m, b) -} -func (m *RegisterRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_RegisterRequest.Marshal(b, m, deterministic) -} -func (m *RegisterRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_RegisterRequest.Merge(m, src) +func (x *RegisterRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *RegisterRequest) XXX_Size() int { - return xxx_messageInfo_RegisterRequest.Size(m) -} -func (m *RegisterRequest) XXX_DiscardUnknown() { - xxx_messageInfo_RegisterRequest.DiscardUnknown(m) + +func (*RegisterRequest) ProtoMessage() {} + +func (x *RegisterRequest) ProtoReflect() protoreflect.Message { + mi := &file_net_lp_rpc_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_RegisterRequest proto.InternalMessageInfo +// Deprecated: Use RegisterRequest.ProtoReflect.Descriptor instead. +func (*RegisterRequest) Descriptor() ([]byte, []int) { + return file_net_lp_rpc_proto_rawDescGZIP(), []int{16} +} -func (m *RegisterRequest) GetSecret() string { - if m != nil { - return m.Secret +func (x *RegisterRequest) GetSecret() string { + if x != nil { + return x.Secret } return "" } -func (m *RegisterRequest) GetCapacity() int64 { - if m != nil { - return m.Capacity +func (x *RegisterRequest) GetCapacity() int64 { + if x != nil { + return x.Capacity } return 0 } -func (m *RegisterRequest) GetCapabilities() *Capabilities { - if m != nil { - return m.Capabilities +func (x *RegisterRequest) GetCapabilities() *Capabilities { + if x != nil { + return x.Capabilities } return nil } // Sent by the orchestrator to the transcoder type NotifySegment struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // URL of the segment to transcode. Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` // Configuration for the transcoding job @@ -1532,67 +1629,75 @@ type NotifySegment struct { TaskId int64 `protobuf:"varint,16,opt,name=taskId,proto3" json:"taskId,omitempty"` // Deprecated by fullProfiles. Set of presets to transcode into. // Should be set to an invalid value to induce failures - Profiles []byte `protobuf:"bytes,17,opt,name=profiles,proto3" json:"profiles,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Profiles []byte `protobuf:"bytes,17,opt,name=profiles,proto3" json:"profiles,omitempty"` } -func (m *NotifySegment) Reset() { *m = NotifySegment{} } -func (m *NotifySegment) String() string { return proto.CompactTextString(m) } -func (*NotifySegment) ProtoMessage() {} -func (*NotifySegment) Descriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{17} +func (x *NotifySegment) Reset() { + *x = NotifySegment{} + if protoimpl.UnsafeEnabled { + mi := &file_net_lp_rpc_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *NotifySegment) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_NotifySegment.Unmarshal(m, b) +func (x *NotifySegment) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *NotifySegment) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_NotifySegment.Marshal(b, m, deterministic) -} -func (m *NotifySegment) XXX_Merge(src proto.Message) { - xxx_messageInfo_NotifySegment.Merge(m, src) -} -func (m *NotifySegment) XXX_Size() int { - return xxx_messageInfo_NotifySegment.Size(m) -} -func (m *NotifySegment) XXX_DiscardUnknown() { - xxx_messageInfo_NotifySegment.DiscardUnknown(m) + +func (*NotifySegment) ProtoMessage() {} + +func (x *NotifySegment) ProtoReflect() protoreflect.Message { + mi := &file_net_lp_rpc_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_NotifySegment proto.InternalMessageInfo +// Deprecated: Use NotifySegment.ProtoReflect.Descriptor instead. +func (*NotifySegment) Descriptor() ([]byte, []int) { + return file_net_lp_rpc_proto_rawDescGZIP(), []int{17} +} -func (m *NotifySegment) GetUrl() string { - if m != nil { - return m.Url +func (x *NotifySegment) GetUrl() string { + if x != nil { + return x.Url } return "" } -func (m *NotifySegment) GetSegData() *SegData { - if m != nil { - return m.SegData +func (x *NotifySegment) GetSegData() *SegData { + if x != nil { + return x.SegData } return nil } -func (m *NotifySegment) GetTaskId() int64 { - if m != nil { - return m.TaskId +func (x *NotifySegment) GetTaskId() int64 { + if x != nil { + return x.TaskId } return 0 } -func (m *NotifySegment) GetProfiles() []byte { - if m != nil { - return m.Profiles +func (x *NotifySegment) GetProfiles() []byte { + if x != nil { + return x.Profiles } return nil } // Required parameters for probabilistic micropayment tickets type TicketParams struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // ETH address of the recipient Recipient []byte `protobuf:"bytes,1,opt,name=recipient,proto3" json:"recipient,omitempty"` // Pay out (in Wei) to the recipient if the ticket wins @@ -1608,183 +1713,203 @@ type TicketParams struct { // Block number at which the current set of advertised TicketParams is no longer valid ExpirationBlock []byte `protobuf:"bytes,6,opt,name=expiration_block,json=expirationBlock,proto3" json:"expiration_block,omitempty"` // Expected ticket expiration params - ExpirationParams *TicketExpirationParams `protobuf:"bytes,7,opt,name=expiration_params,json=expirationParams,proto3" json:"expiration_params,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + ExpirationParams *TicketExpirationParams `protobuf:"bytes,7,opt,name=expiration_params,json=expirationParams,proto3" json:"expiration_params,omitempty"` } -func (m *TicketParams) Reset() { *m = TicketParams{} } -func (m *TicketParams) String() string { return proto.CompactTextString(m) } -func (*TicketParams) ProtoMessage() {} -func (*TicketParams) Descriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{18} +func (x *TicketParams) Reset() { + *x = TicketParams{} + if protoimpl.UnsafeEnabled { + mi := &file_net_lp_rpc_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *TicketParams) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_TicketParams.Unmarshal(m, b) -} -func (m *TicketParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_TicketParams.Marshal(b, m, deterministic) +func (x *TicketParams) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *TicketParams) XXX_Merge(src proto.Message) { - xxx_messageInfo_TicketParams.Merge(m, src) -} -func (m *TicketParams) XXX_Size() int { - return xxx_messageInfo_TicketParams.Size(m) -} -func (m *TicketParams) XXX_DiscardUnknown() { - xxx_messageInfo_TicketParams.DiscardUnknown(m) + +func (*TicketParams) ProtoMessage() {} + +func (x *TicketParams) ProtoReflect() protoreflect.Message { + mi := &file_net_lp_rpc_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_TicketParams proto.InternalMessageInfo +// Deprecated: Use TicketParams.ProtoReflect.Descriptor instead. +func (*TicketParams) Descriptor() ([]byte, []int) { + return file_net_lp_rpc_proto_rawDescGZIP(), []int{18} +} -func (m *TicketParams) GetRecipient() []byte { - if m != nil { - return m.Recipient +func (x *TicketParams) GetRecipient() []byte { + if x != nil { + return x.Recipient } return nil } -func (m *TicketParams) GetFaceValue() []byte { - if m != nil { - return m.FaceValue +func (x *TicketParams) GetFaceValue() []byte { + if x != nil { + return x.FaceValue } return nil } -func (m *TicketParams) GetWinProb() []byte { - if m != nil { - return m.WinProb +func (x *TicketParams) GetWinProb() []byte { + if x != nil { + return x.WinProb } return nil } -func (m *TicketParams) GetRecipientRandHash() []byte { - if m != nil { - return m.RecipientRandHash +func (x *TicketParams) GetRecipientRandHash() []byte { + if x != nil { + return x.RecipientRandHash } return nil } -func (m *TicketParams) GetSeed() []byte { - if m != nil { - return m.Seed +func (x *TicketParams) GetSeed() []byte { + if x != nil { + return x.Seed } return nil } -func (m *TicketParams) GetExpirationBlock() []byte { - if m != nil { - return m.ExpirationBlock +func (x *TicketParams) GetExpirationBlock() []byte { + if x != nil { + return x.ExpirationBlock } return nil } -func (m *TicketParams) GetExpirationParams() *TicketExpirationParams { - if m != nil { - return m.ExpirationParams +func (x *TicketParams) GetExpirationParams() *TicketExpirationParams { + if x != nil { + return x.ExpirationParams } return nil } // Sender Params (nonces and signatures) type TicketSenderParams struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Monotonically increasing counter that makes the ticket // unique relative to a particular hash commitment to a recipient's random number SenderNonce uint32 `protobuf:"varint,1,opt,name=sender_nonce,json=senderNonce,proto3" json:"sender_nonce,omitempty"` // Sender signature over the ticket - Sig []byte `protobuf:"bytes,2,opt,name=sig,proto3" json:"sig,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Sig []byte `protobuf:"bytes,2,opt,name=sig,proto3" json:"sig,omitempty"` } -func (m *TicketSenderParams) Reset() { *m = TicketSenderParams{} } -func (m *TicketSenderParams) String() string { return proto.CompactTextString(m) } -func (*TicketSenderParams) ProtoMessage() {} -func (*TicketSenderParams) Descriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{19} +func (x *TicketSenderParams) Reset() { + *x = TicketSenderParams{} + if protoimpl.UnsafeEnabled { + mi := &file_net_lp_rpc_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *TicketSenderParams) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_TicketSenderParams.Unmarshal(m, b) -} -func (m *TicketSenderParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_TicketSenderParams.Marshal(b, m, deterministic) -} -func (m *TicketSenderParams) XXX_Merge(src proto.Message) { - xxx_messageInfo_TicketSenderParams.Merge(m, src) +func (x *TicketSenderParams) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *TicketSenderParams) XXX_Size() int { - return xxx_messageInfo_TicketSenderParams.Size(m) -} -func (m *TicketSenderParams) XXX_DiscardUnknown() { - xxx_messageInfo_TicketSenderParams.DiscardUnknown(m) + +func (*TicketSenderParams) ProtoMessage() {} + +func (x *TicketSenderParams) ProtoReflect() protoreflect.Message { + mi := &file_net_lp_rpc_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_TicketSenderParams proto.InternalMessageInfo +// Deprecated: Use TicketSenderParams.ProtoReflect.Descriptor instead. +func (*TicketSenderParams) Descriptor() ([]byte, []int) { + return file_net_lp_rpc_proto_rawDescGZIP(), []int{19} +} -func (m *TicketSenderParams) GetSenderNonce() uint32 { - if m != nil { - return m.SenderNonce +func (x *TicketSenderParams) GetSenderNonce() uint32 { + if x != nil { + return x.SenderNonce } return 0 } -func (m *TicketSenderParams) GetSig() []byte { - if m != nil { - return m.Sig +func (x *TicketSenderParams) GetSig() []byte { + if x != nil { + return x.Sig } return nil } // Ticket params for expiration related validation type TicketExpirationParams struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Round during which tickets are created CreationRound int64 `protobuf:"varint,1,opt,name=creation_round,json=creationRound,proto3" json:"creation_round,omitempty"` // Block hash associated with creation_round - CreationRoundBlockHash []byte `protobuf:"bytes,2,opt,name=creation_round_block_hash,json=creationRoundBlockHash,proto3" json:"creation_round_block_hash,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + CreationRoundBlockHash []byte `protobuf:"bytes,2,opt,name=creation_round_block_hash,json=creationRoundBlockHash,proto3" json:"creation_round_block_hash,omitempty"` } -func (m *TicketExpirationParams) Reset() { *m = TicketExpirationParams{} } -func (m *TicketExpirationParams) String() string { return proto.CompactTextString(m) } -func (*TicketExpirationParams) ProtoMessage() {} -func (*TicketExpirationParams) Descriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{20} +func (x *TicketExpirationParams) Reset() { + *x = TicketExpirationParams{} + if protoimpl.UnsafeEnabled { + mi := &file_net_lp_rpc_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *TicketExpirationParams) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_TicketExpirationParams.Unmarshal(m, b) -} -func (m *TicketExpirationParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_TicketExpirationParams.Marshal(b, m, deterministic) +func (x *TicketExpirationParams) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *TicketExpirationParams) XXX_Merge(src proto.Message) { - xxx_messageInfo_TicketExpirationParams.Merge(m, src) -} -func (m *TicketExpirationParams) XXX_Size() int { - return xxx_messageInfo_TicketExpirationParams.Size(m) -} -func (m *TicketExpirationParams) XXX_DiscardUnknown() { - xxx_messageInfo_TicketExpirationParams.DiscardUnknown(m) + +func (*TicketExpirationParams) ProtoMessage() {} + +func (x *TicketExpirationParams) ProtoReflect() protoreflect.Message { + mi := &file_net_lp_rpc_proto_msgTypes[20] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_TicketExpirationParams proto.InternalMessageInfo +// Deprecated: Use TicketExpirationParams.ProtoReflect.Descriptor instead. +func (*TicketExpirationParams) Descriptor() ([]byte, []int) { + return file_net_lp_rpc_proto_rawDescGZIP(), []int{20} +} -func (m *TicketExpirationParams) GetCreationRound() int64 { - if m != nil { - return m.CreationRound +func (x *TicketExpirationParams) GetCreationRound() int64 { + if x != nil { + return x.CreationRound } return 0 } -func (m *TicketExpirationParams) GetCreationRoundBlockHash() []byte { - if m != nil { - return m.CreationRoundBlockHash +func (x *TicketExpirationParams) GetCreationRoundBlockHash() []byte { + if x != nil { + return x.CreationRoundBlockHash } return nil } @@ -1793,6 +1918,10 @@ func (m *TicketExpirationParams) GetCreationRoundBlockHash() []byte { // A payment can constitute of multiple tickets // A broadcaster might need to send multiple tickets to top up his credit with an Orchestrator type Payment struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Probabilistic micropayment ticket parameters // These remain the same even when sending multiple tickets TicketParams *TicketParams `protobuf:"bytes,1,opt,name=ticket_params,json=ticketParams,proto3" json:"ticket_params,omitempty"` @@ -1802,239 +1931,895 @@ type Payment struct { ExpirationParams *TicketExpirationParams `protobuf:"bytes,3,opt,name=expiration_params,json=expirationParams,proto3" json:"expiration_params,omitempty"` TicketSenderParams []*TicketSenderParams `protobuf:"bytes,4,rep,name=ticket_sender_params,json=ticketSenderParams,proto3" json:"ticket_sender_params,omitempty"` // O's last known price - ExpectedPrice *PriceInfo `protobuf:"bytes,5,opt,name=expected_price,json=expectedPrice,proto3" json:"expected_price,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + ExpectedPrice *PriceInfo `protobuf:"bytes,5,opt,name=expected_price,json=expectedPrice,proto3" json:"expected_price,omitempty"` } -func (m *Payment) Reset() { *m = Payment{} } -func (m *Payment) String() string { return proto.CompactTextString(m) } -func (*Payment) ProtoMessage() {} -func (*Payment) Descriptor() ([]byte, []int) { - return fileDescriptor_034e29c79f9ba827, []int{21} +func (x *Payment) Reset() { + *x = Payment{} + if protoimpl.UnsafeEnabled { + mi := &file_net_lp_rpc_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Payment) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Payment.Unmarshal(m, b) +func (x *Payment) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Payment) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Payment.Marshal(b, m, deterministic) -} -func (m *Payment) XXX_Merge(src proto.Message) { - xxx_messageInfo_Payment.Merge(m, src) -} -func (m *Payment) XXX_Size() int { - return xxx_messageInfo_Payment.Size(m) + +func (*Payment) ProtoMessage() {} + +func (x *Payment) ProtoReflect() protoreflect.Message { + mi := &file_net_lp_rpc_proto_msgTypes[21] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (m *Payment) XXX_DiscardUnknown() { - xxx_messageInfo_Payment.DiscardUnknown(m) + +// Deprecated: Use Payment.ProtoReflect.Descriptor instead. +func (*Payment) Descriptor() ([]byte, []int) { + return file_net_lp_rpc_proto_rawDescGZIP(), []int{21} } -var xxx_messageInfo_Payment proto.InternalMessageInfo +func (x *Payment) GetTicketParams() *TicketParams { + if x != nil { + return x.TicketParams + } + return nil +} -func (m *Payment) GetTicketParams() *TicketParams { - if m != nil { - return m.TicketParams +func (x *Payment) GetSender() []byte { + if x != nil { + return x.Sender } return nil } -func (m *Payment) GetSender() []byte { - if m != nil { - return m.Sender +func (x *Payment) GetExpirationParams() *TicketExpirationParams { + if x != nil { + return x.ExpirationParams } return nil } -func (m *Payment) GetExpirationParams() *TicketExpirationParams { - if m != nil { - return m.ExpirationParams +func (x *Payment) GetTicketSenderParams() []*TicketSenderParams { + if x != nil { + return x.TicketSenderParams } return nil } -func (m *Payment) GetTicketSenderParams() []*TicketSenderParams { - if m != nil { - return m.TicketSenderParams +func (x *Payment) GetExpectedPrice() *PriceInfo { + if x != nil { + return x.ExpectedPrice } return nil } -func (m *Payment) GetExpectedPrice() *PriceInfo { - if m != nil { - return m.ExpectedPrice +// Non-binary capability constraints, such as supported ranges. +type Capabilities_Constraints struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Models map[string]*Capabilities_Constraints_ModelConstraint `protobuf:"bytes,1,rep,name=models,proto3" json:"models,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *Capabilities_Constraints) Reset() { + *x = Capabilities_Constraints{} + if protoimpl.UnsafeEnabled { + mi := &file_net_lp_rpc_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Capabilities_Constraints) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Capabilities_Constraints) ProtoMessage() {} + +func (x *Capabilities_Constraints) ProtoReflect() protoreflect.Message { + mi := &file_net_lp_rpc_proto_msgTypes[23] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Capabilities_Constraints.ProtoReflect.Descriptor instead. +func (*Capabilities_Constraints) Descriptor() ([]byte, []int) { + return file_net_lp_rpc_proto_rawDescGZIP(), []int{7, 1} +} + +func (x *Capabilities_Constraints) GetModels() map[string]*Capabilities_Constraints_ModelConstraint { + if x != nil { + return x.Models } return nil } -func init() { - proto.RegisterEnum("net.OSInfo_StorageType", OSInfo_StorageType_name, OSInfo_StorageType_value) - proto.RegisterEnum("net.VideoProfile_Format", VideoProfile_Format_name, VideoProfile_Format_value) - proto.RegisterEnum("net.VideoProfile_Profile", VideoProfile_Profile_name, VideoProfile_Profile_value) - proto.RegisterEnum("net.VideoProfile_VideoCodec", VideoProfile_VideoCodec_name, VideoProfile_VideoCodec_value) - proto.RegisterEnum("net.VideoProfile_ChromaSubsampling", VideoProfile_ChromaSubsampling_name, VideoProfile_ChromaSubsampling_value) - proto.RegisterType((*PingPong)(nil), "net.PingPong") - proto.RegisterType((*EndTranscodingSessionRequest)(nil), "net.EndTranscodingSessionRequest") - proto.RegisterType((*EndTranscodingSessionResponse)(nil), "net.EndTranscodingSessionResponse") - proto.RegisterType((*OrchestratorRequest)(nil), "net.OrchestratorRequest") - proto.RegisterType((*OSInfo)(nil), "net.OSInfo") - proto.RegisterType((*S3OSInfo)(nil), "net.S3OSInfo") - proto.RegisterType((*PriceInfo)(nil), "net.PriceInfo") - proto.RegisterType((*Capabilities)(nil), "net.Capabilities") - proto.RegisterMapType((map[uint32]*Capabilities_CapabilityConstraints)(nil), "net.Capabilities.CapabilityConstraintsEntry") - proto.RegisterMapType((map[uint32]uint32)(nil), "net.Capabilities.CapacitiesEntry") - proto.RegisterType((*Capabilities_Constraints)(nil), "net.Capabilities.Constraints") - proto.RegisterType((*Capabilities_CapabilityConstraints)(nil), "net.Capabilities.CapabilityConstraints") - proto.RegisterMapType((map[string]*Capabilities_CapabilityConstraints_ModelConstraint)(nil), "net.Capabilities.CapabilityConstraints.ModelsEntry") - proto.RegisterType((*Capabilities_CapabilityConstraints_ModelConstraint)(nil), "net.Capabilities.CapabilityConstraints.ModelConstraint") - proto.RegisterType((*OrchestratorInfo)(nil), "net.OrchestratorInfo") - proto.RegisterType((*AuthToken)(nil), "net.AuthToken") - proto.RegisterType((*SegData)(nil), "net.SegData") - proto.RegisterType((*SegParameters)(nil), "net.SegParameters") - proto.RegisterType((*VideoProfile)(nil), "net.VideoProfile") - proto.RegisterType((*TranscodedSegmentData)(nil), "net.TranscodedSegmentData") - proto.RegisterType((*TranscodeData)(nil), "net.TranscodeData") - proto.RegisterType((*TranscodeResult)(nil), "net.TranscodeResult") - proto.RegisterType((*RegisterRequest)(nil), "net.RegisterRequest") - proto.RegisterType((*NotifySegment)(nil), "net.NotifySegment") - proto.RegisterType((*TicketParams)(nil), "net.TicketParams") - proto.RegisterType((*TicketSenderParams)(nil), "net.TicketSenderParams") - proto.RegisterType((*TicketExpirationParams)(nil), "net.TicketExpirationParams") - proto.RegisterType((*Payment)(nil), "net.Payment") -} - -func init() { - proto.RegisterFile("net/lp_rpc.proto", fileDescriptor_034e29c79f9ba827) -} - -var fileDescriptor_034e29c79f9ba827 = []byte{ - // 2021 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x58, 0xdd, 0x72, 0xdb, 0xc6, - 0xf5, 0x17, 0x3f, 0xc4, 0x8f, 0x43, 0x52, 0x82, 0xd6, 0x96, 0x0c, 0x33, 0x76, 0xfe, 0x32, 0x12, - 0xe5, 0xef, 0xcc, 0xd4, 0x8a, 0x87, 0x92, 0x9d, 0xb8, 0x33, 0x99, 0x56, 0x1f, 0xb4, 0xc4, 0xd4, - 0x92, 0x38, 0x4b, 0xd9, 0x33, 0xed, 0x45, 0x59, 0x08, 0x58, 0x92, 0xa8, 0x48, 0x00, 0x5a, 0x2c, - 0x63, 0x29, 0xd3, 0x17, 0xe8, 0x23, 0xb4, 0x37, 0x9d, 0xe9, 0x4c, 0x9f, 0xa0, 0x2f, 0xd0, 0x07, - 0xe8, 0x03, 0xf4, 0x41, 0x7a, 0xdf, 0xce, 0x9e, 0x5d, 0x80, 0x80, 0x48, 0x27, 0xaa, 0xef, 0xf6, - 0x7c, 0xee, 0xd9, 0xb3, 0x67, 0x7f, 0xe7, 0x00, 0x60, 0xf8, 0x4c, 0x7c, 0x35, 0x0e, 0xfb, 0x3c, - 0x74, 0xb6, 0x43, 0x1e, 0x88, 0x80, 0x14, 0x7c, 0x26, 0xac, 0x4d, 0xa8, 0x74, 0x3d, 0x7f, 0xd8, - 0x0d, 0xfc, 0x21, 0xb9, 0x0f, 0xcb, 0xdf, 0xdb, 0xe3, 0x29, 0x33, 0x73, 0x9b, 0xb9, 0xa7, 0x75, - 0xaa, 0x08, 0xeb, 0x04, 0x1e, 0xb5, 0x7d, 0xf7, 0x9c, 0xdb, 0x7e, 0xe4, 0x04, 0xae, 0xe7, 0x0f, - 0x7b, 0x2c, 0x8a, 0xbc, 0xc0, 0xa7, 0xec, 0x6a, 0xca, 0x22, 0x41, 0x9e, 0x01, 0xd8, 0x53, 0x31, - 0xea, 0x8b, 0xe0, 0x92, 0xf9, 0x68, 0x5a, 0x6b, 0xad, 0x6c, 0xfb, 0x4c, 0x6c, 0xef, 0x4d, 0xc5, - 0xe8, 0x5c, 0x72, 0x69, 0xd5, 0x8e, 0x97, 0xd6, 0xff, 0xc1, 0xe3, 0x0f, 0xb8, 0x8b, 0xc2, 0xc0, - 0x8f, 0x98, 0x75, 0x0d, 0xf7, 0xce, 0xb8, 0x33, 0x62, 0x91, 0xe0, 0xb6, 0x08, 0x78, 0xbc, 0x8d, - 0x09, 0x65, 0xdb, 0x75, 0x39, 0x8b, 0x22, 0x1d, 0x5e, 0x4c, 0x12, 0x03, 0x0a, 0x91, 0x37, 0x34, - 0xf3, 0xc8, 0x95, 0x4b, 0xf2, 0x02, 0xea, 0x8e, 0x1d, 0xda, 0x17, 0xde, 0xd8, 0x13, 0x1e, 0x8b, - 0xcc, 0x02, 0x06, 0xb5, 0x86, 0x41, 0x1d, 0xa4, 0x04, 0x34, 0xa3, 0x66, 0xfd, 0x29, 0x07, 0xa5, - 0xb3, 0x5e, 0xc7, 0x1f, 0x04, 0xe4, 0x15, 0xd4, 0x22, 0x11, 0x70, 0x7b, 0xc8, 0xce, 0x6f, 0x42, - 0x95, 0x90, 0x95, 0xd6, 0x03, 0x74, 0xa0, 0x34, 0xb6, 0x7b, 0x33, 0x31, 0x4d, 0xeb, 0x92, 0x2d, - 0x28, 0x45, 0x3b, 0x9e, 0x3f, 0x08, 0x4c, 0x03, 0xb7, 0x6d, 0xa0, 0x55, 0x6f, 0x47, 0xd9, 0x51, - 0x2d, 0xb4, 0x9e, 0x41, 0x2d, 0xe5, 0x82, 0x00, 0x94, 0x0e, 0x3b, 0xb4, 0x7d, 0x70, 0x6e, 0x2c, - 0x91, 0x12, 0xe4, 0x7b, 0x3b, 0x46, 0x4e, 0xf2, 0x8e, 0xce, 0xce, 0x8e, 0xde, 0xb4, 0x8d, 0xbc, - 0xf5, 0xd7, 0x1c, 0x54, 0x62, 0x1f, 0x84, 0x40, 0x71, 0x14, 0x44, 0x02, 0xc3, 0xaa, 0x52, 0x5c, - 0xcb, 0x2c, 0x5c, 0xb2, 0x1b, 0xcc, 0x42, 0x95, 0xca, 0x25, 0xd9, 0x80, 0x52, 0x18, 0x8c, 0x3d, - 0xe7, 0x06, 0xcf, 0x5f, 0xa5, 0x9a, 0x22, 0x8f, 0xa0, 0x1a, 0x79, 0x43, 0xdf, 0x16, 0x53, 0xce, - 0xcc, 0x22, 0x8a, 0x66, 0x0c, 0xf2, 0x29, 0x80, 0xc3, 0x99, 0xcb, 0x7c, 0xe1, 0xd9, 0x63, 0x73, - 0x19, 0xc5, 0x29, 0x0e, 0x69, 0x42, 0xe5, 0x7a, 0x6f, 0xf2, 0xc3, 0xa1, 0x2d, 0x98, 0x59, 0x42, - 0x69, 0x42, 0x5b, 0x6f, 0xa1, 0xda, 0xe5, 0x9e, 0xc3, 0x30, 0x48, 0x0b, 0xea, 0xa1, 0x24, 0xba, - 0x8c, 0xbf, 0xf5, 0x3d, 0x15, 0x6c, 0x81, 0x66, 0x78, 0xe4, 0x73, 0x68, 0x84, 0xde, 0x35, 0x1b, - 0x47, 0xb1, 0x52, 0x1e, 0x95, 0xb2, 0x4c, 0xeb, 0xef, 0x25, 0xa8, 0xa7, 0xaf, 0x4d, 0x9e, 0xe0, - 0xc2, 0x13, 0x91, 0xe0, 0x9e, 0x3f, 0x34, 0x73, 0x9b, 0x85, 0xa7, 0x45, 0x3a, 0x63, 0x90, 0x4d, - 0xa8, 0x4d, 0x6c, 0xdf, 0x95, 0xc5, 0x23, 0x2f, 0x3f, 0x8f, 0xf2, 0x34, 0x8b, 0xec, 0x01, 0xc8, - 0x8b, 0x77, 0xe2, 0xea, 0x28, 0x3c, 0xad, 0xb5, 0x9e, 0xcc, 0x55, 0x07, 0x12, 0x4a, 0xa7, 0xed, - 0x0b, 0x7e, 0x43, 0x53, 0x46, 0xb2, 0x1c, 0xbf, 0x67, 0x5c, 0x16, 0xae, 0x4e, 0x61, 0x4c, 0x92, - 0x5f, 0x40, 0xcd, 0x09, 0x7c, 0x59, 0xbd, 0x9e, 0x2f, 0x22, 0xcc, 0x60, 0xad, 0xf5, 0x78, 0x81, - 0xf7, 0x99, 0x12, 0x4d, 0x5b, 0x90, 0x0b, 0x58, 0x4f, 0xca, 0xf2, 0x26, 0xa5, 0x65, 0x96, 0x30, - 0xd0, 0x9f, 0x2d, 0x0e, 0x74, 0x4e, 0x5d, 0xc5, 0xbc, 0xd8, 0x55, 0xf3, 0x5b, 0x58, 0xbd, 0x75, - 0xba, 0xb8, 0x80, 0xe4, 0x35, 0x35, 0x54, 0x01, 0x25, 0x78, 0x90, 0x47, 0x9e, 0x22, 0x7e, 0x9e, - 0xff, 0x26, 0xd7, 0x7c, 0x06, 0xb5, 0x94, 0x37, 0x59, 0x33, 0x13, 0xcf, 0x7f, 0xa7, 0xf3, 0xa1, - 0xaa, 0x32, 0xc5, 0x69, 0xfe, 0x27, 0x07, 0xeb, 0x0b, 0x63, 0x24, 0xbf, 0x82, 0xd2, 0x24, 0x70, - 0xd9, 0x38, 0xc2, 0x6b, 0xac, 0xb5, 0x76, 0xee, 0x78, 0xb8, 0xed, 0x13, 0xb4, 0x52, 0x67, 0xd4, - 0x2e, 0x9a, 0x5b, 0xb0, 0x8a, 0xec, 0x99, 0x9e, 0x7c, 0x29, 0xef, 0x6d, 0x3e, 0xc1, 0x98, 0x2a, - 0x14, 0xd7, 0x4d, 0x0e, 0xb5, 0x94, 0x75, 0xfa, 0xdc, 0xfa, 0xe1, 0x9c, 0xa4, 0xcf, 0x5d, 0x6b, - 0x7d, 0xfd, 0x3f, 0xc5, 0x34, 0x63, 0xa4, 0x13, 0x76, 0x05, 0xcd, 0x0f, 0x5f, 0xd2, 0x82, 0xd4, - 0x7f, 0x9b, 0x0d, 0xe1, 0xff, 0xef, 0x18, 0x42, 0x6a, 0x4b, 0xeb, 0x1f, 0x79, 0x30, 0xd2, 0x40, - 0x8a, 0x8f, 0xf2, 0x53, 0x00, 0xa1, 0xa1, 0x97, 0xf1, 0xf8, 0xa6, 0x66, 0x1c, 0xf2, 0x12, 0x1a, - 0xc2, 0x73, 0x2e, 0x99, 0xe8, 0x87, 0x36, 0xb7, 0x27, 0x91, 0xde, 0x5f, 0x41, 0xe7, 0x39, 0x4a, - 0xba, 0x28, 0xa0, 0x75, 0x91, 0xa2, 0x64, 0x13, 0xc0, 0x87, 0xdd, 0x47, 0xe0, 0x2b, 0xa4, 0x9a, - 0x40, 0x02, 0x08, 0xb4, 0x1a, 0x26, 0xd8, 0x90, 0x02, 0xf3, 0x62, 0x16, 0xcc, 0x6f, 0x43, 0xf7, - 0xf2, 0x9d, 0xa0, 0xfb, 0x56, 0x13, 0x2a, 0xfd, 0x44, 0x13, 0x22, 0x5b, 0x50, 0xd6, 0x90, 0x6d, - 0x6e, 0x62, 0xdd, 0xd5, 0x52, 0xd0, 0x4e, 0x63, 0x99, 0xf5, 0x3b, 0xa8, 0x26, 0xe6, 0xf2, 0x35, - 0xcc, 0x5a, 0x5c, 0x9d, 0x2a, 0x82, 0x3c, 0x06, 0x88, 0x54, 0x03, 0xeb, 0x7b, 0xae, 0x46, 0xdf, - 0xaa, 0xe6, 0x74, 0x5c, 0x99, 0x6f, 0x76, 0x1d, 0x7a, 0xdc, 0x16, 0xf2, 0x65, 0x14, 0x10, 0xdd, - 0x52, 0x1c, 0xeb, 0xdf, 0x45, 0x28, 0xf7, 0xd8, 0xf0, 0xd0, 0x16, 0x36, 0xbe, 0x22, 0xdb, 0xf7, - 0x06, 0x2c, 0x12, 0x1d, 0x57, 0xef, 0x92, 0xe2, 0x60, 0x9f, 0x63, 0x57, 0x1a, 0x22, 0xe5, 0x12, - 0xfb, 0x80, 0x1d, 0x8d, 0xd0, 0x6f, 0x9d, 0xe2, 0x5a, 0xe2, 0x73, 0xc8, 0x83, 0x81, 0x37, 0x66, - 0x71, 0x6e, 0x13, 0x3a, 0xee, 0x94, 0xcb, 0xb3, 0x4e, 0xd9, 0x84, 0x8a, 0x3b, 0xd5, 0xd1, 0xc9, - 0xac, 0x2d, 0xd3, 0x84, 0x9e, 0xbb, 0x8a, 0xf2, 0xc7, 0x5c, 0x45, 0xe5, 0xa7, 0xae, 0xe2, 0x39, - 0xdc, 0x77, 0xec, 0xb1, 0xd3, 0x0f, 0x19, 0x77, 0x58, 0x28, 0xa6, 0xf6, 0xb8, 0x8f, 0x67, 0x02, - 0x7c, 0xb1, 0x44, 0xca, 0xba, 0x89, 0xe8, 0x58, 0x9e, 0xf0, 0x6e, 0x97, 0x27, 0xc3, 0x1f, 0x4c, - 0xc7, 0xe3, 0x6e, 0x9c, 0x8c, 0x27, 0xa8, 0xab, 0xc2, 0x7f, 0xe7, 0xb9, 0x2c, 0xd0, 0x12, 0x9a, - 0x51, 0x23, 0x5f, 0x43, 0x23, 0x4d, 0xb7, 0x4c, 0xeb, 0x43, 0x76, 0x59, 0xbd, 0xdb, 0x86, 0x3b, - 0xe6, 0x67, 0x77, 0x32, 0xdc, 0x21, 0x7b, 0x40, 0x22, 0x36, 0x9c, 0x30, 0x5f, 0x3f, 0x3a, 0x26, - 0x18, 0x8f, 0xcc, 0x2d, 0x4c, 0x1c, 0x51, 0xc3, 0x03, 0x1b, 0x76, 0x13, 0x09, 0x5d, 0xd3, 0xda, - 0x33, 0x16, 0xd9, 0x06, 0xf2, 0x3a, 0xe0, 0x0e, 0x4b, 0x66, 0x29, 0x4f, 0x36, 0xd3, 0x2f, 0x54, - 0x0a, 0xe7, 0x25, 0xd6, 0x0e, 0x34, 0x32, 0x3e, 0x65, 0x25, 0x0d, 0x78, 0xa0, 0x70, 0xb2, 0x48, - 0x71, 0x4d, 0x56, 0x20, 0x2f, 0x02, 0x2c, 0xb7, 0x22, 0xcd, 0x8b, 0xc0, 0xfa, 0xe7, 0x32, 0xd4, - 0xd3, 0xe7, 0x90, 0x46, 0xbe, 0x3d, 0x61, 0x38, 0xe7, 0x54, 0x29, 0xae, 0xe5, 0x2b, 0x79, 0xef, - 0xb9, 0x62, 0x64, 0xae, 0x61, 0x35, 0x29, 0x42, 0x8e, 0x22, 0x23, 0xe6, 0x0d, 0x47, 0xc2, 0x24, - 0xc8, 0xd6, 0x94, 0xc4, 0x81, 0x0b, 0x4f, 0xc2, 0x13, 0x33, 0xef, 0xa1, 0x20, 0x26, 0x65, 0xa9, - 0x0e, 0xc2, 0xc8, 0xbc, 0xaf, 0x20, 0x71, 0x10, 0x46, 0xe4, 0x39, 0x94, 0x06, 0x01, 0x9f, 0xd8, - 0xc2, 0x5c, 0xc7, 0x69, 0xcc, 0x9c, 0x4b, 0xec, 0xf6, 0x6b, 0x94, 0x53, 0xad, 0x27, 0x77, 0x1d, - 0x84, 0xd1, 0x21, 0xf3, 0xcd, 0x0d, 0x74, 0xa3, 0x29, 0xb2, 0x03, 0x65, 0xfd, 0x24, 0xcc, 0x07, - 0xe8, 0xea, 0xe1, 0xbc, 0xab, 0xf8, 0xae, 0x62, 0x4d, 0x19, 0xd0, 0x30, 0x08, 0x4d, 0x13, 0xc3, - 0x94, 0x4b, 0xf2, 0x12, 0xca, 0xcc, 0x57, 0x40, 0xfa, 0x10, 0xdd, 0x3c, 0x9a, 0x77, 0x83, 0xc4, - 0x41, 0xe0, 0x32, 0x87, 0xc6, 0xca, 0x38, 0x61, 0x05, 0xe3, 0x80, 0x1f, 0xb2, 0x50, 0x8c, 0xcc, - 0x26, 0x3a, 0x4c, 0x71, 0xc8, 0x11, 0xd4, 0x9d, 0x11, 0x0f, 0x26, 0xb6, 0x3a, 0x8e, 0xf9, 0x09, - 0x3a, 0xff, 0x6c, 0xde, 0xf9, 0x01, 0x6a, 0xf5, 0xa6, 0x17, 0x91, 0x3d, 0x09, 0xc7, 0x9e, 0x3f, - 0xa4, 0x19, 0x43, 0x99, 0xdd, 0xab, 0xa9, 0x2d, 0x5b, 0x84, 0xf9, 0x08, 0x13, 0x10, 0x93, 0xd6, - 0x63, 0x28, 0x69, 0x1d, 0x80, 0xd2, 0x49, 0xb7, 0x7d, 0x74, 0xde, 0x33, 0x96, 0x48, 0x19, 0x0a, - 0x27, 0xdd, 0x5d, 0x23, 0x67, 0xfd, 0x1e, 0xca, 0xf1, 0x1d, 0xdf, 0x83, 0xd5, 0xf6, 0xe9, 0xc1, - 0xd9, 0x61, 0x9b, 0xf6, 0x0f, 0xdb, 0xaf, 0xf7, 0xde, 0xbe, 0x91, 0x03, 0xea, 0x1a, 0x34, 0x8e, - 0x5b, 0x2f, 0x77, 0xfb, 0xfb, 0x7b, 0xbd, 0xf6, 0x9b, 0xce, 0x69, 0xdb, 0xc8, 0x91, 0x06, 0x54, - 0x91, 0x75, 0xb2, 0xd7, 0x39, 0x35, 0xf2, 0x09, 0x79, 0xdc, 0x39, 0x3a, 0x36, 0x0a, 0xe4, 0x21, - 0xac, 0x23, 0x79, 0x70, 0x76, 0xda, 0x3b, 0xa7, 0x7b, 0x9d, 0xd3, 0xf6, 0xa1, 0x12, 0x15, 0xad, - 0x16, 0xc0, 0x2c, 0x49, 0xa4, 0x02, 0x45, 0xa9, 0x68, 0x2c, 0xe9, 0xd5, 0x0b, 0x23, 0x27, 0xc3, - 0x7a, 0xd7, 0xfd, 0xc6, 0xc8, 0xab, 0xc5, 0x2b, 0xa3, 0x60, 0x1d, 0xc0, 0xda, 0xdc, 0xd9, 0xc9, - 0x0a, 0xc0, 0xc1, 0x31, 0x3d, 0x3b, 0xd9, 0xeb, 0xef, 0xb6, 0x9e, 0x1b, 0x4b, 0x19, 0xba, 0x65, - 0xe4, 0xd2, 0xf4, 0xee, 0xae, 0x91, 0xb7, 0xae, 0x60, 0x3d, 0xfe, 0x0a, 0x61, 0x6e, 0x4f, 0x3d, - 0x29, 0xc4, 0x61, 0x03, 0x0a, 0x53, 0x3e, 0x8e, 0x07, 0x82, 0x29, 0x1f, 0xe3, 0x24, 0x8d, 0x13, - 0xa9, 0x06, 0x5f, 0x4d, 0x91, 0x6d, 0xb8, 0x77, 0x0b, 0xb6, 0xfa, 0xd2, 0x52, 0x8d, 0xdb, 0x6b, - 0x61, 0x06, 0xb6, 0xde, 0xf2, 0xb1, 0xf5, 0x6b, 0x68, 0x24, 0x5b, 0xe2, 0x56, 0x2f, 0xa1, 0xa2, - 0x1f, 0x73, 0x3c, 0x00, 0x35, 0x55, 0xa7, 0x5d, 0x14, 0x18, 0x4d, 0x74, 0xe7, 0x3f, 0x79, 0xac, - 0x3f, 0xe7, 0x60, 0x35, 0xb1, 0xa2, 0x2c, 0x9a, 0x8e, 0x45, 0xdc, 0x30, 0x72, 0xb3, 0x86, 0xb1, - 0x01, 0xcb, 0x8c, 0xf3, 0x80, 0xab, 0x46, 0x75, 0xbc, 0x44, 0x15, 0x49, 0x9e, 0x42, 0xd1, 0xb5, - 0x85, 0xad, 0x1b, 0x37, 0xc9, 0xc6, 0x20, 0xf7, 0x3e, 0x5e, 0xa2, 0xa8, 0x41, 0xbe, 0x84, 0x62, - 0xea, 0xdb, 0x66, 0x5d, 0x21, 0xef, 0xad, 0x29, 0x83, 0xa2, 0xca, 0x7e, 0x05, 0x4a, 0x1c, 0x03, - 0xb1, 0xfe, 0x00, 0xab, 0x94, 0x0d, 0xbd, 0x48, 0xb0, 0xe4, 0x73, 0x6e, 0x03, 0x4a, 0x11, 0x73, - 0x38, 0x8b, 0x3f, 0x62, 0x34, 0x25, 0x1b, 0x92, 0x9e, 0xb2, 0x6f, 0x74, 0xb2, 0x13, 0xfa, 0x63, - 0x3f, 0xeb, 0xfe, 0x98, 0x83, 0xc6, 0x69, 0x20, 0xbc, 0xc1, 0x8d, 0x4e, 0xe6, 0x82, 0x1b, 0xfe, - 0x02, 0xca, 0x91, 0x6a, 0xc3, 0xda, 0x6b, 0x3d, 0x06, 0x5e, 0xcc, 0x7c, 0x2c, 0x94, 0x61, 0x0b, - 0x3b, 0xba, 0xec, 0xb8, 0x98, 0x80, 0x02, 0xd5, 0x54, 0xa6, 0xeb, 0xae, 0x65, 0xbb, 0xee, 0x77, - 0xc5, 0x4a, 0xde, 0x28, 0x7c, 0x57, 0xac, 0x3c, 0x31, 0x2c, 0xeb, 0x2f, 0x79, 0xa8, 0xa7, 0xc7, - 0x28, 0xf9, 0x29, 0xc3, 0x99, 0xe3, 0x85, 0x1e, 0xf3, 0x85, 0xee, 0xf9, 0x33, 0x86, 0x9c, 0x2e, - 0x06, 0xb6, 0xc3, 0xfa, 0xb3, 0x59, 0xb0, 0x4e, 0xab, 0x92, 0xf3, 0x4e, 0x32, 0xc8, 0x43, 0xa8, - 0xbc, 0xf7, 0xfc, 0x7e, 0xc8, 0x83, 0x0b, 0x3d, 0x03, 0x94, 0xdf, 0x7b, 0x7e, 0x97, 0x07, 0x17, - 0xb2, 0x34, 0x13, 0x37, 0x7d, 0x6e, 0xfb, 0xae, 0xea, 0xaa, 0x6a, 0x22, 0x58, 0x4b, 0x44, 0xd4, - 0xf6, 0x5d, 0x6c, 0xaa, 0x04, 0x8a, 0x11, 0x63, 0xae, 0x9e, 0x0d, 0x70, 0x4d, 0xbe, 0x04, 0x63, - 0x36, 0xaa, 0xf4, 0x2f, 0xc6, 0x81, 0x73, 0x89, 0x43, 0x42, 0x9d, 0xae, 0xce, 0xf8, 0xfb, 0x92, - 0x4d, 0x8e, 0x61, 0x2d, 0xa5, 0xaa, 0x67, 0x47, 0x35, 0x30, 0x7c, 0x92, 0x9a, 0x1d, 0xdb, 0x89, - 0x8e, 0x9e, 0x22, 0x53, 0x1b, 0x28, 0x8e, 0xd5, 0x01, 0xa2, 0x74, 0x7b, 0xcc, 0x77, 0x19, 0xd7, - 0x69, 0x7a, 0x02, 0xf5, 0x08, 0xe9, 0xbe, 0x1f, 0xf8, 0x0e, 0xd3, 0xa3, 0x72, 0x4d, 0xf1, 0x4e, - 0x25, 0x6b, 0xc1, 0x9b, 0xf8, 0x01, 0x36, 0x16, 0x6f, 0x4b, 0xb6, 0x60, 0xc5, 0xe1, 0x4c, 0x05, - 0xcb, 0x83, 0xa9, 0xef, 0xea, 0x47, 0xd2, 0x88, 0xb9, 0x54, 0x32, 0xc9, 0x2b, 0x78, 0x98, 0x55, - 0x53, 0x49, 0x50, 0xa9, 0x54, 0x1b, 0x6d, 0x64, 0x2c, 0x30, 0x19, 0x32, 0x9f, 0xd6, 0xdf, 0xf2, - 0x50, 0xee, 0xda, 0x37, 0x58, 0x6e, 0x73, 0x43, 0x75, 0xee, 0x6e, 0x43, 0x35, 0xbe, 0x11, 0x79, - 0x40, 0xbd, 0x97, 0xa6, 0x16, 0x27, 0xbb, 0xf0, 0x11, 0xc9, 0x26, 0x1d, 0xb8, 0xaf, 0x23, 0xd3, - 0xd9, 0xd5, 0xce, 0x8a, 0x88, 0x45, 0x0f, 0x52, 0xce, 0xd2, 0xb7, 0x41, 0x89, 0x98, 0xbf, 0xa1, - 0x17, 0xb0, 0xc2, 0xae, 0x43, 0xe6, 0x08, 0xe6, 0xf6, 0x71, 0xd0, 0xd7, 0xa3, 0xfb, 0xed, 0xaf, - 0x80, 0x46, 0xac, 0x85, 0xac, 0xd6, 0xbf, 0x72, 0x50, 0x4f, 0xe3, 0x07, 0xd9, 0x87, 0xd5, 0x23, - 0x26, 0x32, 0x2c, 0x73, 0x0e, 0x65, 0x34, 0x8a, 0x34, 0x17, 0xe3, 0x0f, 0xf9, 0x2d, 0xac, 0x2f, - 0xfc, 0xc7, 0x44, 0xd4, 0x47, 0xfe, 0x8f, 0xfd, 0xce, 0x6a, 0x5a, 0x3f, 0xa6, 0xa2, 0x7e, 0x51, - 0x91, 0xcf, 0xa1, 0xd8, 0x95, 0x2d, 0x47, 0xfd, 0xda, 0x89, 0xff, 0x9f, 0x35, 0xb3, 0x64, 0xeb, - 0x14, 0xe0, 0x7c, 0xf6, 0x65, 0xf5, 0x4b, 0x20, 0x31, 0x06, 0xa6, 0xb8, 0xf7, 0xd1, 0xe4, 0x16, - 0x38, 0x36, 0x15, 0x00, 0x67, 0x30, 0xeb, 0x79, 0x6e, 0xbf, 0xfc, 0x9b, 0xe5, 0xed, 0xaf, 0x7c, - 0x26, 0x2e, 0x4a, 0xf8, 0xff, 0x6e, 0xe7, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xa2, 0xc3, 0xc1, - 0x2e, 0xd3, 0x13, 0x00, 0x00, +type Capabilities_Constraints_ModelConstraint struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Warm bool `protobuf:"varint,1,opt,name=warm,proto3" json:"warm,omitempty"` +} + +func (x *Capabilities_Constraints_ModelConstraint) Reset() { + *x = Capabilities_Constraints_ModelConstraint{} + if protoimpl.UnsafeEnabled { + mi := &file_net_lp_rpc_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Capabilities_Constraints_ModelConstraint) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Capabilities_Constraints_ModelConstraint) ProtoMessage() {} + +func (x *Capabilities_Constraints_ModelConstraint) ProtoReflect() protoreflect.Message { + mi := &file_net_lp_rpc_proto_msgTypes[25] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Capabilities_Constraints_ModelConstraint.ProtoReflect.Descriptor instead. +func (*Capabilities_Constraints_ModelConstraint) Descriptor() ([]byte, []int) { + return file_net_lp_rpc_proto_rawDescGZIP(), []int{7, 1, 0} +} + +func (x *Capabilities_Constraints_ModelConstraint) GetWarm() bool { + if x != nil { + return x.Warm + } + return false +} + +var File_net_lp_rpc_proto protoreflect.FileDescriptor + +var file_net_lp_rpc_proto_rawDesc = []byte{ + 0x0a, 0x10, 0x6e, 0x65, 0x74, 0x2f, 0x6c, 0x70, 0x5f, 0x72, 0x70, 0x63, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x03, 0x6e, 0x65, 0x74, 0x22, 0x20, 0x0a, 0x08, 0x50, 0x69, 0x6e, 0x67, 0x50, + 0x6f, 0x6e, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x4d, 0x0a, 0x1c, 0x45, 0x6e, 0x64, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x0a, 0x61, 0x75, 0x74, + 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, + 0x6e, 0x65, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x09, 0x61, + 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x1f, 0x0a, 0x1d, 0x45, 0x6e, 0x64, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x78, 0x0a, 0x13, 0x4f, 0x72, 0x63, + 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, + 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x12, 0x35, 0x0a, 0x0c, + 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, + 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, + 0x69, 0x65, 0x73, 0x22, 0x99, 0x01, 0x0a, 0x06, 0x4f, 0x53, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x39, + 0x0a, 0x0b, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4f, 0x53, 0x49, 0x6e, 0x66, 0x6f, + 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x73, 0x74, + 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x25, 0x0a, 0x06, 0x73, 0x33, 0x69, + 0x6e, 0x66, 0x6f, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x6e, 0x65, 0x74, 0x2e, + 0x53, 0x33, 0x4f, 0x53, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x73, 0x33, 0x69, 0x6e, 0x66, 0x6f, + 0x22, 0x2d, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x0a, 0x0a, 0x06, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x10, 0x00, 0x12, 0x06, 0x0a, 0x02, 0x53, + 0x33, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x47, 0x4f, 0x4f, 0x47, 0x4c, 0x45, 0x10, 0x02, 0x22, + 0xa2, 0x01, 0x0a, 0x08, 0x53, 0x33, 0x4f, 0x53, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, + 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, + 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x72, + 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x78, 0x41, 0x6d, 0x7a, + 0x44, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x78, 0x41, 0x6d, 0x7a, + 0x44, 0x61, 0x74, 0x65, 0x22, 0x55, 0x0a, 0x09, 0x50, 0x72, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x72, 0x69, 0x63, 0x65, 0x50, 0x65, 0x72, 0x55, 0x6e, 0x69, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x70, 0x72, 0x69, 0x63, 0x65, 0x50, 0x65, + 0x72, 0x55, 0x6e, 0x69, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x50, + 0x65, 0x72, 0x55, 0x6e, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x70, 0x69, + 0x78, 0x65, 0x6c, 0x73, 0x50, 0x65, 0x72, 0x55, 0x6e, 0x69, 0x74, 0x22, 0xd9, 0x04, 0x0a, 0x0c, + 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, + 0x62, 0x69, 0x74, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x03, 0x28, 0x04, 0x52, + 0x09, 0x62, 0x69, 0x74, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x20, 0x0a, 0x0b, 0x6d, 0x61, + 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, + 0x0b, 0x6d, 0x61, 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x12, 0x41, 0x0a, 0x0a, + 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x21, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, + 0x69, 0x65, 0x73, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x0a, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, + 0x44, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, + 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, + 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, + 0x61, 0x69, 0x6e, 0x74, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, + 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x1a, 0xe1, 0x01, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, + 0x69, 0x6e, 0x74, 0x73, 0x12, 0x41, 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, + 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, + 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x1a, 0x25, 0x0a, 0x0f, 0x4d, 0x6f, 0x64, 0x65, 0x6c, + 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x77, 0x61, + 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x77, 0x61, 0x72, 0x6d, 0x1a, 0x68, + 0x0a, 0x0b, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x43, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, + 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, + 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x6f, + 0x64, 0x65, 0x6c, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5d, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x73, + 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x33, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, + 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, + 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc0, 0x02, 0x0a, 0x10, 0x4f, 0x72, 0x63, 0x68, + 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1e, 0x0a, 0x0a, + 0x74, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x36, 0x0a, 0x0d, + 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x0c, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x12, 0x2d, 0x0a, 0x0a, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x6e, + 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, + 0x72, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x70, 0x72, 0x69, 0x63, 0x65, 0x49, + 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x35, 0x0a, + 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, + 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, + 0x74, 0x69, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, + 0x65, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x41, + 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x12, 0x25, 0x0a, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x20, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4f, 0x53, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x22, 0x60, 0x0a, 0x09, 0x41, 0x75, + 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1d, 0x0a, + 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, + 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xf4, 0x04, 0x0a, + 0x07, 0x53, 0x65, 0x67, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x6e, 0x69, + 0x66, 0x65, 0x73, 0x74, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6d, 0x61, + 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x65, 0x71, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x73, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, + 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1a, + 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, + 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x35, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61, + 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, + 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, + 0x73, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, + 0x2d, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x30, + 0x0a, 0x14, 0x63, 0x61, 0x6c, 0x63, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, + 0x6c, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x63, 0x61, + 0x6c, 0x63, 0x50, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x48, 0x61, 0x73, 0x68, + 0x12, 0x25, 0x0a, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x20, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x0b, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4f, 0x53, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, + 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x35, 0x0a, 0x0c, 0x66, 0x75, 0x6c, 0x6c, 0x50, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x21, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, + 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x52, 0x0c, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x37, + 0x0a, 0x0d, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x32, 0x18, + 0x22, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, 0x65, + 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x0d, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x72, + 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x32, 0x12, 0x37, 0x0a, 0x0d, 0x66, 0x75, 0x6c, 0x6c, 0x50, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x33, 0x18, 0x23, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, + 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, + 0x65, 0x52, 0x0d, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x33, + 0x12, 0x41, 0x0a, 0x12, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x25, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6e, + 0x65, 0x74, 0x2e, 0x53, 0x65, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, + 0x52, 0x11, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, + 0x65, 0x72, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x26, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x12, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x69, + 0x6e, 0x69, 0x74, 0x22, 0x33, 0x0a, 0x0d, 0x53, 0x65, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, + 0x74, 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x74, 0x6f, 0x22, 0xcc, 0x05, 0x0a, 0x0c, 0x56, 0x69, 0x64, + 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x11, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x12, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x62, + 0x69, 0x74, 0x72, 0x61, 0x74, 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x62, 0x69, + 0x74, 0x72, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x70, 0x73, 0x18, 0x14, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x03, 0x66, 0x70, 0x73, 0x12, 0x30, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, + 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x46, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x70, 0x73, + 0x44, 0x65, 0x6e, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x66, 0x70, 0x73, 0x44, 0x65, + 0x6e, 0x12, 0x33, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x17, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, + 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x07, 0x70, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x6f, 0x70, 0x18, 0x18, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x03, 0x67, 0x6f, 0x70, 0x12, 0x36, 0x0a, 0x07, 0x65, 0x6e, 0x63, 0x6f, + 0x64, 0x65, 0x72, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x6e, 0x65, 0x74, 0x2e, + 0x56, 0x69, 0x64, 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x56, 0x69, 0x64, + 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x52, 0x07, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, + 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x44, 0x65, 0x70, 0x74, 0x68, 0x18, 0x1a, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x44, 0x65, 0x70, 0x74, 0x68, + 0x12, 0x47, 0x0a, 0x0c, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x18, 0x1b, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x56, 0x69, 0x64, + 0x65, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x61, + 0x53, 0x75, 0x62, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x52, 0x0c, 0x63, 0x68, 0x72, + 0x6f, 0x6d, 0x61, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x71, 0x75, 0x61, + 0x6c, 0x69, 0x74, 0x79, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x71, 0x75, 0x61, 0x6c, + 0x69, 0x74, 0x79, 0x22, 0x1d, 0x0a, 0x06, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x0a, 0x0a, + 0x06, 0x4d, 0x50, 0x45, 0x47, 0x54, 0x53, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4d, 0x50, 0x34, + 0x10, 0x01, 0x22, 0x6a, 0x0a, 0x07, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x13, 0x0a, + 0x0f, 0x45, 0x4e, 0x43, 0x4f, 0x44, 0x45, 0x52, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, + 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x48, 0x32, 0x36, 0x34, 0x5f, 0x42, 0x41, 0x53, 0x45, 0x4c, + 0x49, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x48, 0x32, 0x36, 0x34, 0x5f, 0x4d, 0x41, + 0x49, 0x4e, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x48, 0x32, 0x36, 0x34, 0x5f, 0x48, 0x49, 0x47, + 0x48, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x48, 0x32, 0x36, 0x34, 0x5f, 0x43, 0x4f, 0x4e, 0x53, + 0x54, 0x52, 0x41, 0x49, 0x4e, 0x45, 0x44, 0x5f, 0x48, 0x49, 0x47, 0x48, 0x10, 0x04, 0x22, 0x32, + 0x0a, 0x0a, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x12, 0x08, 0x0a, 0x04, + 0x48, 0x32, 0x36, 0x34, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x32, 0x36, 0x35, 0x10, 0x01, + 0x12, 0x07, 0x0a, 0x03, 0x56, 0x50, 0x38, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x56, 0x50, 0x39, + 0x10, 0x03, 0x22, 0x43, 0x0a, 0x11, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x53, 0x75, 0x62, 0x73, + 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x48, 0x52, 0x4f, 0x4d, + 0x41, 0x5f, 0x34, 0x32, 0x30, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x48, 0x52, 0x4f, 0x4d, + 0x41, 0x5f, 0x34, 0x32, 0x32, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x48, 0x52, 0x4f, 0x4d, + 0x41, 0x5f, 0x34, 0x34, 0x34, 0x10, 0x02, 0x22, 0x71, 0x0a, 0x15, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x63, 0x6f, 0x64, 0x65, 0x64, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, + 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, + 0x72, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x06, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x70, 0x65, + 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x75, 0x72, + 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, + 0x75, 0x61, 0x6c, 0x48, 0x61, 0x73, 0x68, 0x55, 0x72, 0x6c, 0x22, 0x59, 0x0a, 0x0d, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x36, 0x0a, 0x08, 0x73, + 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x53, 0x65, + 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x73, 0x65, 0x67, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x03, 0x73, 0x69, 0x67, 0x22, 0x9a, 0x01, 0x0a, 0x0f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, + 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x65, 0x71, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x73, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x05, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x12, 0x28, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x12, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, + 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x29, 0x0a, + 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6e, 0x65, + 0x74, 0x2e, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x42, 0x08, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x22, 0x7c, 0x0a, 0x0f, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x35, 0x0a, 0x0c, 0x63, 0x61, 0x70, + 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, + 0x65, 0x73, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, + 0x22, 0x89, 0x01, 0x0a, 0x0d, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, 0x65, 0x67, 0x6d, 0x65, + 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x75, 0x72, 0x6c, 0x12, 0x26, 0x0a, 0x07, 0x73, 0x65, 0x67, 0x44, 0x61, 0x74, 0x61, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x65, 0x67, 0x44, + 0x61, 0x74, 0x61, 0x52, 0x07, 0x73, 0x65, 0x67, 0x44, 0x61, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x06, + 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x18, 0x10, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x74, 0x61, + 0x73, 0x6b, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, + 0x18, 0x11, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, + 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x21, 0x10, 0x22, 0x22, 0x9f, 0x02, 0x0a, + 0x0c, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x1c, 0x0a, + 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x66, + 0x61, 0x63, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x09, 0x66, 0x61, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x77, 0x69, + 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x62, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x77, 0x69, + 0x6e, 0x50, 0x72, 0x6f, 0x62, 0x12, 0x2e, 0x0a, 0x13, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, + 0x6e, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x64, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x11, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x6e, + 0x64, 0x48, 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x65, 0x64, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x04, 0x73, 0x65, 0x65, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x78, 0x70, + 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x48, 0x0a, 0x11, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1b, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x70, 0x69, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x10, 0x65, 0x78, + 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x49, + 0x0a, 0x12, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x6e, + 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x73, 0x65, 0x6e, 0x64, + 0x65, 0x72, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x22, 0x7a, 0x0a, 0x16, 0x54, 0x69, 0x63, + 0x6b, 0x65, 0x74, 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x39, 0x0a, 0x19, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x16, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x48, 0x61, 0x73, 0x68, 0x22, 0xa5, 0x02, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x12, 0x36, 0x0a, 0x0d, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, + 0x69, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x0c, 0x74, 0x69, 0x63, + 0x6b, 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, + 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, + 0x72, 0x12, 0x48, 0x0a, 0x11, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6e, + 0x65, 0x74, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x10, 0x65, 0x78, 0x70, 0x69, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x49, 0x0a, 0x14, 0x74, + 0x69, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6e, 0x65, 0x74, 0x2e, + 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x73, 0x52, 0x12, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x35, 0x0a, 0x0e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, + 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x72, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, + 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x72, 0x69, 0x63, 0x65, 0x32, 0xd8, 0x01, + 0x0a, 0x0c, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x42, + 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x12, 0x18, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x6e, 0x65, + 0x74, 0x2e, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, + 0x66, 0x6f, 0x12, 0x5e, 0x0a, 0x15, 0x45, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, + 0x64, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x2e, 0x6e, 0x65, + 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, + 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, + 0x69, 0x6e, 0x67, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x0d, 0x2e, 0x6e, 0x65, 0x74, + 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6e, 0x67, 0x1a, 0x0d, 0x2e, 0x6e, 0x65, 0x74, 0x2e, + 0x50, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6e, 0x67, 0x32, 0x4e, 0x0a, 0x0a, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x40, 0x0a, 0x12, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x65, 0x72, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x14, 0x2e, 0x6e, + 0x65, 0x74, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, + 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x42, 0x07, 0x5a, 0x05, 0x2e, 0x2f, 0x6e, 0x65, + 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_net_lp_rpc_proto_rawDescOnce sync.Once + file_net_lp_rpc_proto_rawDescData = file_net_lp_rpc_proto_rawDesc +) + +func file_net_lp_rpc_proto_rawDescGZIP() []byte { + file_net_lp_rpc_proto_rawDescOnce.Do(func() { + file_net_lp_rpc_proto_rawDescData = protoimpl.X.CompressGZIP(file_net_lp_rpc_proto_rawDescData) + }) + return file_net_lp_rpc_proto_rawDescData +} + +var file_net_lp_rpc_proto_enumTypes = make([]protoimpl.EnumInfo, 5) +var file_net_lp_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 27) +var file_net_lp_rpc_proto_goTypes = []interface{}{ + (OSInfo_StorageType)(0), // 0: net.OSInfo.StorageType + (VideoProfile_Format)(0), // 1: net.VideoProfile.Format + (VideoProfile_Profile)(0), // 2: net.VideoProfile.Profile + (VideoProfile_VideoCodec)(0), // 3: net.VideoProfile.VideoCodec + (VideoProfile_ChromaSubsampling)(0), // 4: net.VideoProfile.ChromaSubsampling + (*PingPong)(nil), // 5: net.PingPong + (*EndTranscodingSessionRequest)(nil), // 6: net.EndTranscodingSessionRequest + (*EndTranscodingSessionResponse)(nil), // 7: net.EndTranscodingSessionResponse + (*OrchestratorRequest)(nil), // 8: net.OrchestratorRequest + (*OSInfo)(nil), // 9: net.OSInfo + (*S3OSInfo)(nil), // 10: net.S3OSInfo + (*PriceInfo)(nil), // 11: net.PriceInfo + (*Capabilities)(nil), // 12: net.Capabilities + (*OrchestratorInfo)(nil), // 13: net.OrchestratorInfo + (*AuthToken)(nil), // 14: net.AuthToken + (*SegData)(nil), // 15: net.SegData + (*SegParameters)(nil), // 16: net.SegParameters + (*VideoProfile)(nil), // 17: net.VideoProfile + (*TranscodedSegmentData)(nil), // 18: net.TranscodedSegmentData + (*TranscodeData)(nil), // 19: net.TranscodeData + (*TranscodeResult)(nil), // 20: net.TranscodeResult + (*RegisterRequest)(nil), // 21: net.RegisterRequest + (*NotifySegment)(nil), // 22: net.NotifySegment + (*TicketParams)(nil), // 23: net.TicketParams + (*TicketSenderParams)(nil), // 24: net.TicketSenderParams + (*TicketExpirationParams)(nil), // 25: net.TicketExpirationParams + (*Payment)(nil), // 26: net.Payment + nil, // 27: net.Capabilities.CapacitiesEntry + (*Capabilities_Constraints)(nil), // 28: net.Capabilities.Constraints + nil, // 29: net.Capabilities.ConstraintsEntry + (*Capabilities_Constraints_ModelConstraint)(nil), // 30: net.Capabilities.Constraints.ModelConstraint + nil, // 31: net.Capabilities.Constraints.ModelsEntry +} +var file_net_lp_rpc_proto_depIdxs = []int32{ + 14, // 0: net.EndTranscodingSessionRequest.auth_token:type_name -> net.AuthToken + 12, // 1: net.OrchestratorRequest.capabilities:type_name -> net.Capabilities + 0, // 2: net.OSInfo.storageType:type_name -> net.OSInfo.StorageType + 10, // 3: net.OSInfo.s3info:type_name -> net.S3OSInfo + 27, // 4: net.Capabilities.capacities:type_name -> net.Capabilities.CapacitiesEntry + 29, // 5: net.Capabilities.constraints:type_name -> net.Capabilities.ConstraintsEntry + 23, // 6: net.OrchestratorInfo.ticket_params:type_name -> net.TicketParams + 11, // 7: net.OrchestratorInfo.price_info:type_name -> net.PriceInfo + 12, // 8: net.OrchestratorInfo.capabilities:type_name -> net.Capabilities + 14, // 9: net.OrchestratorInfo.auth_token:type_name -> net.AuthToken + 9, // 10: net.OrchestratorInfo.storage:type_name -> net.OSInfo + 12, // 11: net.SegData.capabilities:type_name -> net.Capabilities + 14, // 12: net.SegData.auth_token:type_name -> net.AuthToken + 9, // 13: net.SegData.storage:type_name -> net.OSInfo + 17, // 14: net.SegData.fullProfiles:type_name -> net.VideoProfile + 17, // 15: net.SegData.fullProfiles2:type_name -> net.VideoProfile + 17, // 16: net.SegData.fullProfiles3:type_name -> net.VideoProfile + 16, // 17: net.SegData.segment_parameters:type_name -> net.SegParameters + 1, // 18: net.VideoProfile.format:type_name -> net.VideoProfile.Format + 2, // 19: net.VideoProfile.profile:type_name -> net.VideoProfile.Profile + 3, // 20: net.VideoProfile.encoder:type_name -> net.VideoProfile.VideoCodec + 4, // 21: net.VideoProfile.chromaFormat:type_name -> net.VideoProfile.ChromaSubsampling + 18, // 22: net.TranscodeData.segments:type_name -> net.TranscodedSegmentData + 19, // 23: net.TranscodeResult.data:type_name -> net.TranscodeData + 13, // 24: net.TranscodeResult.info:type_name -> net.OrchestratorInfo + 12, // 25: net.RegisterRequest.capabilities:type_name -> net.Capabilities + 15, // 26: net.NotifySegment.segData:type_name -> net.SegData + 25, // 27: net.TicketParams.expiration_params:type_name -> net.TicketExpirationParams + 23, // 28: net.Payment.ticket_params:type_name -> net.TicketParams + 25, // 29: net.Payment.expiration_params:type_name -> net.TicketExpirationParams + 24, // 30: net.Payment.ticket_sender_params:type_name -> net.TicketSenderParams + 11, // 31: net.Payment.expected_price:type_name -> net.PriceInfo + 31, // 32: net.Capabilities.Constraints.models:type_name -> net.Capabilities.Constraints.ModelsEntry + 28, // 33: net.Capabilities.ConstraintsEntry.value:type_name -> net.Capabilities.Constraints + 30, // 34: net.Capabilities.Constraints.ModelsEntry.value:type_name -> net.Capabilities.Constraints.ModelConstraint + 8, // 35: net.Orchestrator.GetOrchestrator:input_type -> net.OrchestratorRequest + 6, // 36: net.Orchestrator.EndTranscodingSession:input_type -> net.EndTranscodingSessionRequest + 5, // 37: net.Orchestrator.Ping:input_type -> net.PingPong + 21, // 38: net.Transcoder.RegisterTranscoder:input_type -> net.RegisterRequest + 13, // 39: net.Orchestrator.GetOrchestrator:output_type -> net.OrchestratorInfo + 7, // 40: net.Orchestrator.EndTranscodingSession:output_type -> net.EndTranscodingSessionResponse + 5, // 41: net.Orchestrator.Ping:output_type -> net.PingPong + 22, // 42: net.Transcoder.RegisterTranscoder:output_type -> net.NotifySegment + 39, // [39:43] is the sub-list for method output_type + 35, // [35:39] is the sub-list for method input_type + 35, // [35:35] is the sub-list for extension type_name + 35, // [35:35] is the sub-list for extension extendee + 0, // [0:35] is the sub-list for field type_name +} + +func init() { file_net_lp_rpc_proto_init() } +func file_net_lp_rpc_proto_init() { + if File_net_lp_rpc_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_net_lp_rpc_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PingPong); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_lp_rpc_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EndTranscodingSessionRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_lp_rpc_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EndTranscodingSessionResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_lp_rpc_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OrchestratorRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_lp_rpc_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OSInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_lp_rpc_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*S3OSInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_lp_rpc_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PriceInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_lp_rpc_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Capabilities); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_lp_rpc_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OrchestratorInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_lp_rpc_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AuthToken); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_lp_rpc_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SegData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_lp_rpc_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SegParameters); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_lp_rpc_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*VideoProfile); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_lp_rpc_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TranscodedSegmentData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_lp_rpc_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TranscodeData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_lp_rpc_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TranscodeResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_lp_rpc_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_lp_rpc_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NotifySegment); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_lp_rpc_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TicketParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_lp_rpc_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TicketSenderParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_lp_rpc_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TicketExpirationParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_lp_rpc_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Payment); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_lp_rpc_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Capabilities_Constraints); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_lp_rpc_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Capabilities_Constraints_ModelConstraint); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_net_lp_rpc_proto_msgTypes[15].OneofWrappers = []interface{}{ + (*TranscodeResult_Error)(nil), + (*TranscodeResult_Data)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_net_lp_rpc_proto_rawDesc, + NumEnums: 5, + NumMessages: 27, + NumExtensions: 0, + NumServices: 2, + }, + GoTypes: file_net_lp_rpc_proto_goTypes, + DependencyIndexes: file_net_lp_rpc_proto_depIdxs, + EnumInfos: file_net_lp_rpc_proto_enumTypes, + MessageInfos: file_net_lp_rpc_proto_msgTypes, + }.Build() + File_net_lp_rpc_proto = out.File + file_net_lp_rpc_proto_rawDesc = nil + file_net_lp_rpc_proto_goTypes = nil + file_net_lp_rpc_proto_depIdxs = nil } diff --git a/net/lp_rpc.proto b/net/lp_rpc.proto index bd3e6f1ef..0eddc9fee 100644 --- a/net/lp_rpc.proto +++ b/net/lp_rpc.proto @@ -108,17 +108,8 @@ message Capabilities { // Capacity corresponding to each capability map capacities = 3; - string version = 4; - - Constraints constraints = 5; - - // Non-binary general constraints. - message Constraints { - string minVersion = 1; - } - // Non-binary capability constraints, such as supported ranges. - message CapabilityConstraints { + message Constraints { message ModelConstraint { bool warm = 1; } @@ -126,7 +117,7 @@ message Capabilities { map models = 1; } - map capabilityConstraints = 6; + map constraints = 4; } // The orchestrator sends this in response to `GetOrchestrator`, containing diff --git a/net/lp_rpc_grpc.pb.go b/net/lp_rpc_grpc.pb.go index fa1d80d2e..3743d7975 100644 --- a/net/lp_rpc_grpc.pb.go +++ b/net/lp_rpc_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.5.1 -// - protoc v3.21.12 +// - protoc-gen-go-grpc v1.2.0 +// - protoc v4.25.2 // source: net/lp_rpc.proto package net @@ -15,20 +15,12 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.64.0 or later. -const _ = grpc.SupportPackageIsVersion9 - -const ( - Orchestrator_GetOrchestrator_FullMethodName = "/net.Orchestrator/GetOrchestrator" - Orchestrator_EndTranscodingSession_FullMethodName = "/net.Orchestrator/EndTranscodingSession" - Orchestrator_Ping_FullMethodName = "/net.Orchestrator/Ping" -) +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 // OrchestratorClient is the client API for Orchestrator service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -// -// RPC calls implemented by the orchestrator type OrchestratorClient interface { // Called by the broadcaster to request transcoder info from an orchestrator. GetOrchestrator(ctx context.Context, in *OrchestratorRequest, opts ...grpc.CallOption) (*OrchestratorInfo, error) @@ -45,9 +37,8 @@ func NewOrchestratorClient(cc grpc.ClientConnInterface) OrchestratorClient { } func (c *orchestratorClient) GetOrchestrator(ctx context.Context, in *OrchestratorRequest, opts ...grpc.CallOption) (*OrchestratorInfo, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(OrchestratorInfo) - err := c.cc.Invoke(ctx, Orchestrator_GetOrchestrator_FullMethodName, in, out, cOpts...) + err := c.cc.Invoke(ctx, "/net.Orchestrator/GetOrchestrator", in, out, opts...) if err != nil { return nil, err } @@ -55,9 +46,8 @@ func (c *orchestratorClient) GetOrchestrator(ctx context.Context, in *Orchestrat } func (c *orchestratorClient) EndTranscodingSession(ctx context.Context, in *EndTranscodingSessionRequest, opts ...grpc.CallOption) (*EndTranscodingSessionResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(EndTranscodingSessionResponse) - err := c.cc.Invoke(ctx, Orchestrator_EndTranscodingSession_FullMethodName, in, out, cOpts...) + err := c.cc.Invoke(ctx, "/net.Orchestrator/EndTranscodingSession", in, out, opts...) if err != nil { return nil, err } @@ -65,9 +55,8 @@ func (c *orchestratorClient) EndTranscodingSession(ctx context.Context, in *EndT } func (c *orchestratorClient) Ping(ctx context.Context, in *PingPong, opts ...grpc.CallOption) (*PingPong, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(PingPong) - err := c.cc.Invoke(ctx, Orchestrator_Ping_FullMethodName, in, out, cOpts...) + err := c.cc.Invoke(ctx, "/net.Orchestrator/Ping", in, out, opts...) if err != nil { return nil, err } @@ -76,9 +65,7 @@ func (c *orchestratorClient) Ping(ctx context.Context, in *PingPong, opts ...grp // OrchestratorServer is the server API for Orchestrator service. // All implementations must embed UnimplementedOrchestratorServer -// for forward compatibility. -// -// RPC calls implemented by the orchestrator +// for forward compatibility type OrchestratorServer interface { // Called by the broadcaster to request transcoder info from an orchestrator. GetOrchestrator(context.Context, *OrchestratorRequest) (*OrchestratorInfo, error) @@ -87,12 +74,9 @@ type OrchestratorServer interface { mustEmbedUnimplementedOrchestratorServer() } -// UnimplementedOrchestratorServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedOrchestratorServer struct{} +// UnimplementedOrchestratorServer must be embedded to have forward compatible implementations. +type UnimplementedOrchestratorServer struct { +} func (UnimplementedOrchestratorServer) GetOrchestrator(context.Context, *OrchestratorRequest) (*OrchestratorInfo, error) { return nil, status.Errorf(codes.Unimplemented, "method GetOrchestrator not implemented") @@ -104,7 +88,6 @@ func (UnimplementedOrchestratorServer) Ping(context.Context, *PingPong) (*PingPo return nil, status.Errorf(codes.Unimplemented, "method Ping not implemented") } func (UnimplementedOrchestratorServer) mustEmbedUnimplementedOrchestratorServer() {} -func (UnimplementedOrchestratorServer) testEmbeddedByValue() {} // UnsafeOrchestratorServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to OrchestratorServer will @@ -114,13 +97,6 @@ type UnsafeOrchestratorServer interface { } func RegisterOrchestratorServer(s grpc.ServiceRegistrar, srv OrchestratorServer) { - // If the following call pancis, it indicates UnimplementedOrchestratorServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } s.RegisterService(&Orchestrator_ServiceDesc, srv) } @@ -134,7 +110,7 @@ func _Orchestrator_GetOrchestrator_Handler(srv interface{}, ctx context.Context, } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: Orchestrator_GetOrchestrator_FullMethodName, + FullMethod: "/net.Orchestrator/GetOrchestrator", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(OrchestratorServer).GetOrchestrator(ctx, req.(*OrchestratorRequest)) @@ -152,7 +128,7 @@ func _Orchestrator_EndTranscodingSession_Handler(srv interface{}, ctx context.Co } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: Orchestrator_EndTranscodingSession_FullMethodName, + FullMethod: "/net.Orchestrator/EndTranscodingSession", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(OrchestratorServer).EndTranscodingSession(ctx, req.(*EndTranscodingSessionRequest)) @@ -170,7 +146,7 @@ func _Orchestrator_Ping_Handler(srv interface{}, ctx context.Context, dec func(i } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: Orchestrator_Ping_FullMethodName, + FullMethod: "/net.Orchestrator/Ping", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(OrchestratorServer).Ping(ctx, req.(*PingPong)) @@ -202,17 +178,13 @@ var Orchestrator_ServiceDesc = grpc.ServiceDesc{ Metadata: "net/lp_rpc.proto", } -const ( - Transcoder_RegisterTranscoder_FullMethodName = "/net.Transcoder/RegisterTranscoder" -) - // TranscoderClient is the client API for Transcoder service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type TranscoderClient interface { // Called by the transcoder to register to an orchestrator. The orchestrator // notifies registered transcoders of segments as they come in. - RegisterTranscoder(ctx context.Context, in *RegisterRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[NotifySegment], error) + RegisterTranscoder(ctx context.Context, in *RegisterRequest, opts ...grpc.CallOption) (Transcoder_RegisterTranscoderClient, error) } type transcoderClient struct { @@ -223,13 +195,12 @@ func NewTranscoderClient(cc grpc.ClientConnInterface) TranscoderClient { return &transcoderClient{cc} } -func (c *transcoderClient) RegisterTranscoder(ctx context.Context, in *RegisterRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[NotifySegment], error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - stream, err := c.cc.NewStream(ctx, &Transcoder_ServiceDesc.Streams[0], Transcoder_RegisterTranscoder_FullMethodName, cOpts...) +func (c *transcoderClient) RegisterTranscoder(ctx context.Context, in *RegisterRequest, opts ...grpc.CallOption) (Transcoder_RegisterTranscoderClient, error) { + stream, err := c.cc.NewStream(ctx, &Transcoder_ServiceDesc.Streams[0], "/net.Transcoder/RegisterTranscoder", opts...) if err != nil { return nil, err } - x := &grpc.GenericClientStream[RegisterRequest, NotifySegment]{ClientStream: stream} + x := &transcoderRegisterTranscoderClient{stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } @@ -239,31 +210,41 @@ func (c *transcoderClient) RegisterTranscoder(ctx context.Context, in *RegisterR return x, nil } -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type Transcoder_RegisterTranscoderClient = grpc.ServerStreamingClient[NotifySegment] +type Transcoder_RegisterTranscoderClient interface { + Recv() (*NotifySegment, error) + grpc.ClientStream +} + +type transcoderRegisterTranscoderClient struct { + grpc.ClientStream +} + +func (x *transcoderRegisterTranscoderClient) Recv() (*NotifySegment, error) { + m := new(NotifySegment) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} // TranscoderServer is the server API for Transcoder service. // All implementations must embed UnimplementedTranscoderServer -// for forward compatibility. +// for forward compatibility type TranscoderServer interface { // Called by the transcoder to register to an orchestrator. The orchestrator // notifies registered transcoders of segments as they come in. - RegisterTranscoder(*RegisterRequest, grpc.ServerStreamingServer[NotifySegment]) error + RegisterTranscoder(*RegisterRequest, Transcoder_RegisterTranscoderServer) error mustEmbedUnimplementedTranscoderServer() } -// UnimplementedTranscoderServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedTranscoderServer struct{} +// UnimplementedTranscoderServer must be embedded to have forward compatible implementations. +type UnimplementedTranscoderServer struct { +} -func (UnimplementedTranscoderServer) RegisterTranscoder(*RegisterRequest, grpc.ServerStreamingServer[NotifySegment]) error { +func (UnimplementedTranscoderServer) RegisterTranscoder(*RegisterRequest, Transcoder_RegisterTranscoderServer) error { return status.Errorf(codes.Unimplemented, "method RegisterTranscoder not implemented") } func (UnimplementedTranscoderServer) mustEmbedUnimplementedTranscoderServer() {} -func (UnimplementedTranscoderServer) testEmbeddedByValue() {} // UnsafeTranscoderServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to TranscoderServer will @@ -273,13 +254,6 @@ type UnsafeTranscoderServer interface { } func RegisterTranscoderServer(s grpc.ServiceRegistrar, srv TranscoderServer) { - // If the following call pancis, it indicates UnimplementedTranscoderServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } s.RegisterService(&Transcoder_ServiceDesc, srv) } @@ -288,11 +262,21 @@ func _Transcoder_RegisterTranscoder_Handler(srv interface{}, stream grpc.ServerS if err := stream.RecvMsg(m); err != nil { return err } - return srv.(TranscoderServer).RegisterTranscoder(m, &grpc.GenericServerStream[RegisterRequest, NotifySegment]{ServerStream: stream}) + return srv.(TranscoderServer).RegisterTranscoder(m, &transcoderRegisterTranscoderServer{stream}) +} + +type Transcoder_RegisterTranscoderServer interface { + Send(*NotifySegment) error + grpc.ServerStream } -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type Transcoder_RegisterTranscoderServer = grpc.ServerStreamingServer[NotifySegment] +type transcoderRegisterTranscoderServer struct { + grpc.ServerStream +} + +func (x *transcoderRegisterTranscoderServer) Send(m *NotifySegment) error { + return x.ServerStream.SendMsg(m) +} // Transcoder_ServiceDesc is the grpc.ServiceDesc for Transcoder service. // It's only intended for direct use with grpc.RegisterService, diff --git a/net/redeemer.pb.go b/net/redeemer.pb.go index 87d1869b4..132a70cf4 100644 --- a/net/redeemer.pb.go +++ b/net/redeemer.pb.go @@ -319,6 +319,7 @@ func file_net_redeemer_proto_init() { if File_net_redeemer_proto != nil { return } + file_net_lp_rpc_proto_init() if !protoimpl.UnsafeEnabled { file_net_redeemer_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Ticket); i { diff --git a/pm/recipient.go b/pm/recipient.go index 4c2028175..1c2333a5b 100644 --- a/pm/recipient.go +++ b/pm/recipient.go @@ -43,7 +43,7 @@ type Recipient interface { RedeemWinningTicket(ticket *Ticket, sig []byte, seed *big.Int) error // TicketParams returns the recipient's currently accepted ticket parameters - // for a provided sender ETH address + // for a provided sender ETH adddress TicketParams(sender ethcommon.Address, price *big.Rat) (*TicketParams, error) // TxCostMultiplier returns the tx cost multiplier for an address diff --git a/pm/sender.go b/pm/sender.go index 6f6acf0fd..db178bfc5 100644 --- a/pm/sender.go +++ b/pm/sender.go @@ -113,7 +113,7 @@ func (s *sender) CreateTicketBatch(sessionID string, size int) (*TicketBatch, er ticketParams := &session.ticketParams expirationParams := ticketParams.ExpirationParams - // Ensure backwards compatibility + // Ensure backwards compatbility // If no expirationParams are included by O // B sets the values based upon its last seen round if expirationParams == nil || expirationParams.CreationRound == 0 || expirationParams.CreationRoundBlockHash == (ethcommon.Hash{}) { diff --git a/pm/stub.go b/pm/stub.go index a3e0f7f22..856bb9416 100644 --- a/pm/stub.go +++ b/pm/stub.go @@ -466,7 +466,7 @@ func (m *MockRecipient) RedeemWinningTicket(ticket *Ticket, sig []byte, seed *bi } // TicketParams returns the recipient's currently accepted ticket parameters -// for a provided sender ETH address +// for a provided sender ETH adddress func (m *MockRecipient) TicketParams(sender ethcommon.Address, price *big.Rat) (*TicketParams, error) { args := m.Called(sender, price) diff --git a/server/ai_session.go b/server/ai_session.go index 3a6c28207..6274e397d 100644 --- a/server/ai_session.go +++ b/server/ai_session.go @@ -259,7 +259,7 @@ func (sel *AISessionSelector) Refresh(ctx context.Context) error { var coldSessions []*BroadcastSession for _, sess := range sessions { // If the constraints are missing for this capability skip this session - constraints, ok := sess.OrchestratorInfo.Capabilities.CapabilityConstraints[uint32(sel.cap)] + constraints, ok := sess.OrchestratorInfo.Capabilities.Constraints[uint32(sel.cap)] if !ok { continue } @@ -288,7 +288,7 @@ func (sel *AISessionSelector) Refresh(ctx context.Context) error { func (sel *AISessionSelector) getSessions(ctx context.Context) ([]*BroadcastSession, error) { // No warm constraints applied here because we don't want to filter out orchs based on warm criteria at discovery time // Instead, we want all orchs that support the model and then will filter for orchs that have a warm model separately - capabilityConstraints := map[core.Capability]*core.PerCapabilityConstraints{ + constraints := map[core.Capability]*core.Constraints{ sel.cap: { Models: map[string]*core.ModelConstraint{ sel.modelID: { @@ -297,8 +297,7 @@ func (sel *AISessionSelector) getSessions(ctx context.Context) ([]*BroadcastSess }, }, } - caps := core.NewCapabilitiesWithConstraints(append(core.DefaultCapabilities(), sel.cap), nil, core.Constraints{}, capabilityConstraints) - caps.SetMinVersionConstraint(sel.node.Capabilities.MinVersionConstraint()) + caps := core.NewCapabilitiesWithConstraints(append(core.DefaultCapabilities(), sel.cap), nil, constraints) // Set numOrchs to the pool size so that discovery tries to find maximum # of compatible orchs within a timeout numOrchs := sel.node.OrchestratorPool.Size() diff --git a/server/broadcast.go b/server/broadcast.go index df854cc48..bb0a8344f 100755 --- a/server/broadcast.go +++ b/server/broadcast.go @@ -56,7 +56,7 @@ var submitMultiSession = func(ctx context.Context, sess *BroadcastSession, seg * var maxTranscodeAttempts = errors.New("hit max transcode attempts") type BroadcastConfig struct { - maxPrice *core.AutoConvertedPrice + maxPrice *big.Rat mu sync.RWMutex } @@ -68,19 +68,16 @@ type SegFlightMetadata struct { func (cfg *BroadcastConfig) MaxPrice() *big.Rat { cfg.mu.RLock() defer cfg.mu.RUnlock() - if cfg.maxPrice == nil { - return nil - } - return cfg.maxPrice.Value() + return cfg.maxPrice } -func (cfg *BroadcastConfig) SetMaxPrice(price *core.AutoConvertedPrice) { +func (cfg *BroadcastConfig) SetMaxPrice(price *big.Rat) { cfg.mu.Lock() defer cfg.mu.Unlock() - prevPrice := cfg.maxPrice cfg.maxPrice = price - if prevPrice != nil { - prevPrice.Stop() + + if monitor.Enabled { + monitor.MaxTranscodingPrice(price) } } @@ -288,9 +285,7 @@ func (sp *SessionPool) selectSessions(ctx context.Context, sessionsNum int) []*B checkSessions := func(m *SessionPool) bool { numSess := m.sel.Size() - refreshThreshold := int(math.Min(maxRefreshSessionsThreshold, math.Ceil(float64(m.numOrchs)/2.0))) - clog.Infof(ctx, "Checking if the session refresh is needed, numSess=%v, refreshThreshold=%v", numSess, refreshThreshold) - if numSess < refreshThreshold { + if numSess < int(math.Min(maxRefreshSessionsThreshold, math.Ceil(float64(m.numOrchs)/2.0))) { go m.refreshSessions(ctx) } return (numSess > 0 || len(sp.lastSess) > 0) @@ -450,9 +445,6 @@ func (bsm *BroadcastSessionsManager) shouldSkipVerification(sessions []*Broadcas } func NewSessionManager(ctx context.Context, node *core.LivepeerNode, params *core.StreamParameters, sel BroadcastSessionsSelectorFactory) *BroadcastSessionsManager { - if node.Capabilities != nil { - params.Capabilities.SetMinVersionConstraint(node.Capabilities.MinVersionConstraint()) - } var trustedPoolSize, untrustedPoolSize float64 if node.OrchestratorPool != nil { trustedPoolSize = float64(node.OrchestratorPool.SizeWith(common.ScoreAtLeast(common.Score_Trusted))) diff --git a/server/handlers.go b/server/handlers.go index e95315654..23992a165 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -23,7 +23,6 @@ import ( "github.com/livepeer/go-livepeer/core" "github.com/livepeer/go-livepeer/eth" "github.com/livepeer/go-livepeer/eth/types" - "github.com/livepeer/go-livepeer/monitor" "github.com/livepeer/go-livepeer/pm" "github.com/livepeer/lpms/ffmpeg" "github.com/pkg/errors" @@ -32,12 +31,6 @@ import ( const MainnetChainId = 1 const RinkebyChainId = 4 -func (s *LivepeerServer) healthzHandler() http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - respondOk(w, nil) - }) -} - // Status func (s *LivepeerServer) statusHandler() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -135,7 +128,6 @@ func setBroadcastConfigHandler() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { pricePerUnit := r.FormValue("maxPricePerUnit") pixelsPerUnit := r.FormValue("pixelsPerUnit") - currency := r.FormValue("currency") transcodingOptions := r.FormValue("transcodingOptions") if (pricePerUnit == "" || pixelsPerUnit == "") && transcodingOptions == "" { @@ -145,38 +137,28 @@ func setBroadcastConfigHandler() http.Handler { // set max price if pricePerUnit != "" && pixelsPerUnit != "" { - pr, ok := new(big.Rat).SetString(pricePerUnit) - if !ok { - respond400(w, fmt.Sprintf("Error parsing pricePerUnit value: %s", pricePerUnit)) + pr, err := strconv.ParseInt(pricePerUnit, 10, 64) + if err != nil { + respond400(w, errors.Wrapf(err, "Error converting string to int64").Error()) return } - px, ok := new(big.Rat).SetString(pixelsPerUnit) - if !ok { - respond400(w, fmt.Sprintf("Error parsing pixelsPerUnit value: %s", pixelsPerUnit)) + px, err := strconv.ParseInt(pixelsPerUnit, 10, 64) + if err != nil { + respond400(w, errors.Wrapf(err, "Error converting string to int64").Error()) return } - if px.Sign() <= 0 { - respond400(w, fmt.Sprintf("pixels per unit must be greater than 0, provided %v", pixelsPerUnit)) + if px <= 0 { + respond400(w, fmt.Sprintf("pixels per unit must be greater than 0, provided %d", px)) return } - pricePerPixel := new(big.Rat).Quo(pr, px) - - var autoPrice *core.AutoConvertedPrice - if pricePerPixel.Sign() > 0 { - var err error - autoPrice, err = core.NewAutoConvertedPrice(currency, pricePerPixel, func(price *big.Rat) { - if monitor.Enabled { - monitor.MaxTranscodingPrice(price) - } - glog.Infof("Maximum transcoding price: %v wei per pixel\n", price.FloatString(3)) - }) - if err != nil { - respond400(w, errors.Wrap(err, "error converting price").Error()) - return - } + + var price *big.Rat + if pr > 0 { + price = big.NewRat(pr, px) } - BroadcastCfg.SetMaxPrice(autoPrice) + BroadcastCfg.SetMaxPrice(price) + glog.Infof("Maximum transcoding price: %d per %q pixels\n", pr, px) } // set broadcast profiles @@ -312,8 +294,7 @@ func (s *LivepeerServer) activateOrchestratorHandler(client eth.LivepeerEthClien return } - pricePerUnit, pixelsPerUnit, currency := r.FormValue("pricePerUnit"), r.FormValue("pixelsPerUnit"), r.FormValue("currency") - if err := s.setOrchestratorPriceInfo("default", pricePerUnit, pixelsPerUnit, currency); err != nil { + if err := s.setOrchestratorPriceInfo("default", r.FormValue("pricePerUnit"), r.FormValue("pixelsPerUnit")); err != nil { respond400(w, err.Error()) return } @@ -407,9 +388,8 @@ func (s *LivepeerServer) setOrchestratorConfigHandler(client eth.LivepeerEthClie return mustHaveClient(client, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { pixels := r.FormValue("pixelsPerUnit") price := r.FormValue("pricePerUnit") - currency := r.FormValue("currency") if pixels != "" && price != "" { - if err := s.setOrchestratorPriceInfo("default", price, pixels, currency); err != nil { + if err := s.setOrchestratorPriceInfo("default", price, pixels); err != nil { respond400(w, err.Error()) return } @@ -481,43 +461,53 @@ func (s *LivepeerServer) setOrchestratorConfigHandler(client eth.LivepeerEthClie })) } -func (s *LivepeerServer) setOrchestratorPriceInfo(broadcasterEthAddr, pricePerUnitStr, pixelsPerUnitStr, currency string) error { - ok, err := regexp.MatchString("^0x[0-9a-fA-F]{40}|default$", broadcasterEthAddr) +func (s *LivepeerServer) setOrchestratorPriceInfo(broadcasterEthAddr, pricePerUnitStr, pixelsPerUnitStr string) error { + ok, err := regexp.MatchString("^[0-9]+$", pricePerUnitStr) if err != nil { return err } if !ok { - return fmt.Errorf("broadcasterEthAddr is not a valid eth address, provided %v", broadcasterEthAddr) + return fmt.Errorf("pricePerUnit is not a valid integer, provided %v", pricePerUnitStr) } - pricePerUnit, ok := new(big.Rat).SetString(pricePerUnitStr) - if !ok { - return fmt.Errorf("error parsing pricePerUnit value: %s", pricePerUnitStr) + ok, err = regexp.MatchString("^[0-9]+$", pixelsPerUnitStr) + if err != nil { + return err } - if pricePerUnit.Sign() < 0 { - return fmt.Errorf("price unit must be greater than or equal to 0, provided %s", pricePerUnitStr) + if !ok { + return fmt.Errorf("pixelsPerUnit is not a valid integer, provided %v", pixelsPerUnitStr) } - pixelsPerUnit, ok := new(big.Rat).SetString(pixelsPerUnitStr) + ok, err = regexp.MatchString("^0x[0-9a-fA-F]{40}|default$", broadcasterEthAddr) + if err != nil { + return err + } if !ok { - return fmt.Errorf("error parsing pixelsPerUnit value: %v", pixelsPerUnitStr) + return fmt.Errorf("broadcasterEthAddr is not a valid eth address, provided %v", broadcasterEthAddr) } - if pixelsPerUnit.Sign() <= 0 { - return fmt.Errorf("pixels per unit must be greater than 0, provided %s", pixelsPerUnitStr) + + pricePerUnit, err := strconv.ParseInt(pricePerUnitStr, 10, 64) + if err != nil { + return fmt.Errorf("error converting pricePerUnit string to int64: %v", err) + } + if pricePerUnit < 0 { + return fmt.Errorf("price unit must be greater than or equal to 0, provided %d", pricePerUnit) } - pricePerPixel := new(big.Rat).Quo(pricePerUnit, pixelsPerUnit) - autoPrice, err := core.NewAutoConvertedPrice(currency, pricePerPixel, func(price *big.Rat) { - if broadcasterEthAddr == "default" { - glog.Infof("Price: %v wei per pixel\n ", price.FloatString(3)) - } else { - glog.Infof("Price: %v wei per pixel for broadcaster %v", price.FloatString(3), broadcasterEthAddr) - } - }) + pixelsPerUnit, err := strconv.ParseInt(pixelsPerUnitStr, 10, 64) if err != nil { - return fmt.Errorf("error converting price: %v", err) + return fmt.Errorf("error converting pixelsPerUnit string to int64: %v", err) + } + if pixelsPerUnit <= 0 { + return fmt.Errorf("pixels per unit must be greater than 0, provided %d", pixelsPerUnit) + } + + s.LivepeerNode.SetBasePrice(broadcasterEthAddr, big.NewRat(pricePerUnit, pixelsPerUnit)) + if broadcasterEthAddr == "default" { + glog.Infof("Price per pixel set to %d wei for %d pixels\n", pricePerUnit, pixelsPerUnit) + } else { + glog.Infof("Price per pixel set to %d wei for %d pixels for broadcaster %s\n", pricePerUnit, pixelsPerUnit, broadcasterEthAddr) } - s.LivepeerNode.SetBasePrice(broadcasterEthAddr, autoPrice) return nil } @@ -577,10 +567,9 @@ func (s *LivepeerServer) setPriceForBroadcaster() http.Handler { if s.LivepeerNode.NodeType == core.OrchestratorNode { pricePerUnitStr := r.FormValue("pricePerUnit") pixelsPerUnitStr := r.FormValue("pixelsPerUnit") - currency := r.FormValue("currency") broadcasterEthAddr := r.FormValue("broadcasterEthAddr") - err := s.setOrchestratorPriceInfo(broadcasterEthAddr, pricePerUnitStr, pixelsPerUnitStr, currency) + err := s.setOrchestratorPriceInfo(broadcasterEthAddr, pricePerUnitStr, pixelsPerUnitStr) if err == nil { respondOk(w, []byte(fmt.Sprintf("Price per pixel set to %s wei for %s pixels for broadcaster %s\n", pricePerUnitStr, pixelsPerUnitStr, broadcasterEthAddr))) } else { diff --git a/server/handlers_test.go b/server/handlers_test.go index d191d0a75..aa01fe88c 100644 --- a/server/handlers_test.go +++ b/server/handlers_test.go @@ -116,7 +116,7 @@ func TestOrchestratorInfoHandler_Success(t *testing.T) { s := &LivepeerServer{LivepeerNode: n} price := big.NewRat(1, 2) - s.LivepeerNode.SetBasePrice("default", core.NewFixedPrice(price)) + s.LivepeerNode.SetBasePrice("default", price) trans := &types.Transcoder{ ServiceURI: "127.0.0.1:8935", @@ -196,7 +196,7 @@ func TestSetBroadcastConfigHandler_ConvertPricePerUnitError(t *testing.T) { }) assert.Equal(http.StatusBadRequest, status) - assert.Contains(body, "Error parsing pricePerUnit value") + assert.Contains(body, "Error converting string to int64") } func TestSetBroadcastConfigHandler_ConvertPixelsPerUnitError(t *testing.T) { @@ -209,7 +209,7 @@ func TestSetBroadcastConfigHandler_ConvertPixelsPerUnitError(t *testing.T) { }) assert.Equal(http.StatusBadRequest, status) - assert.Contains(body, "Error parsing pixelsPerUnit value") + assert.Contains(body, "Error converting string to int64") } func TestSetBroadcastConfigHandler_NegativePixelPerUnitError(t *testing.T) { @@ -259,7 +259,7 @@ func TestSetBroadcastConfigHandler_Success(t *testing.T) { func TestGetBroadcastConfigHandler(t *testing.T) { assert := assert.New(t) - BroadcastCfg.maxPrice = core.NewFixedPrice(big.NewRat(1, 2)) + BroadcastCfg.maxPrice = big.NewRat(1, 2) BroadcastJobVideoProfiles = []ffmpeg.VideoProfile{ ffmpeg.VideoProfileLookup["P240p25fps16x9"], } @@ -501,32 +501,27 @@ func TestSetOrchestratorPriceInfo(t *testing.T) { s := stubServer() // pricePerUnit is not an integer - err := s.setOrchestratorPriceInfo("default", "nil", "1", "") + err := s.setOrchestratorPriceInfo("default", "nil", "1") assert.Error(t, err) - assert.Contains(t, err.Error(), "error parsing pricePerUnit value") + assert.True(t, strings.Contains(err.Error(), "pricePerUnit is not a valid integer")) // pixelsPerUnit is not an integer - err = s.setOrchestratorPriceInfo("default", "1", "nil", "") + err = s.setOrchestratorPriceInfo("default", "1", "nil") assert.Error(t, err) - assert.Contains(t, err.Error(), "error parsing pixelsPerUnit value") + assert.True(t, strings.Contains(err.Error(), "pixelsPerUnit is not a valid integer")) - // price feed watcher is not initialized and one attempts a custom currency - err = s.setOrchestratorPriceInfo("default", "1e12", "0.7", "USD") - assert.Error(t, err) - assert.Contains(t, err.Error(), "PriceFeedWatcher is not initialized") - - err = s.setOrchestratorPriceInfo("default", "1", "1", "") + err = s.setOrchestratorPriceInfo("default", "1", "1") assert.Nil(t, err) assert.Zero(t, s.LivepeerNode.GetBasePrice("default").Cmp(big.NewRat(1, 1))) - err = s.setOrchestratorPriceInfo("default", "-5", "1", "") - assert.EqualError(t, err, fmt.Sprintf("price unit must be greater than or equal to 0, provided %d", -5)) + err = s.setOrchestratorPriceInfo("default", "-5", "1") + assert.EqualErrorf(t, err, err.Error(), "price unit must be greater than or equal to 0, provided %d\n", -5) // pixels per unit <= 0 - err = s.setOrchestratorPriceInfo("default", "1", "0", "") - assert.EqualError(t, err, fmt.Sprintf("pixels per unit must be greater than 0, provided %d", 0)) - err = s.setOrchestratorPriceInfo("default", "1", "-5", "") - assert.EqualError(t, err, fmt.Sprintf("pixels per unit must be greater than 0, provided %d", -5)) + err = s.setOrchestratorPriceInfo("default", "1", "0") + assert.EqualErrorf(t, err, err.Error(), "pixels per unit must be greater than 0, provided %d\n", 0) + err = s.setOrchestratorPriceInfo("default", "1", "-5") + assert.EqualErrorf(t, err, err.Error(), "pixels per unit must be greater than 0, provided %d\n", -5) } func TestSetPriceForBroadcasterHandler(t *testing.T) { diff --git a/server/mediaserver.go b/server/mediaserver.go index 1e1b1be15..72a445ffb 100644 --- a/server/mediaserver.go +++ b/server/mediaserver.go @@ -44,15 +44,12 @@ import ( "github.com/patrickmn/go-cache" ) -var ( - errAlreadyExists = errors.New("StreamAlreadyExists") - errStorage = errors.New("ErrStorage") - errDiscovery = errors.New("ErrDiscovery") - errNoOrchs = errors.New("ErrNoOrchs") - errUnknownStream = errors.New("ErrUnknownStream") - errMismatchedParams = errors.New("Mismatched type for stream params") - errForbidden = errors.New("authentication denied") -) +var errAlreadyExists = errors.New("StreamAlreadyExists") +var errStorage = errors.New("ErrStorage") +var errDiscovery = errors.New("ErrDiscovery") +var errNoOrchs = errors.New("ErrNoOrchs") +var errUnknownStream = errors.New("ErrUnknownStream") +var errMismatchedParams = errors.New("Mismatched type for stream params") const HLSWaitInterval = time.Second const HLSBufferCap = uint(43200) //12 hrs assuming 1s segment @@ -206,9 +203,6 @@ func (s *LivepeerServer) StartMediaServer(ctx context.Context, httpAddr string) // Store ctx to later use as cancel signal for watchdog goroutine s.context = ctx - // health endpoint - s.HTTPMux.Handle("/healthz", s.healthzHandler()) - //LPMS handlers for handling RTMP video s.LPMS.HandleRTMPPublish(createRTMPStreamIDHandler(ctx, s, nil), gotRTMPStreamHandler(s), endRTMPStreamHandler(s)) s.LPMS.HandleRTMPPlay(getRTMPStreamHandler(s)) @@ -246,8 +240,8 @@ func (s *LivepeerServer) StartMediaServer(ctx context.Context, httpAddr string) } // RTMP Publish Handlers -func createRTMPStreamIDHandler(_ctx context.Context, s *LivepeerServer, webhookResponseOverride *authWebhookResponse) func(url *url.URL) (strmID stream.AppData, e error) { - return func(url *url.URL) (strmID stream.AppData, e error) { +func createRTMPStreamIDHandler(_ctx context.Context, s *LivepeerServer, webhookResponseOverride *authWebhookResponse) func(url *url.URL) (strmID stream.AppData) { + return func(url *url.URL) (strmID stream.AppData) { //Check HTTP header for ManifestID //If ManifestID is passed in HTTP header, use that one //Else check webhook for ManifestID @@ -269,8 +263,8 @@ func createRTMPStreamIDHandler(_ctx context.Context, s *LivepeerServer, webhookR // do not replace captured _ctx variable ctx := clog.AddNonce(_ctx, nonce) if resp, err = authenticateStream(AuthWebhookURL, url.String()); err != nil { - clog.Errorf(ctx, fmt.Sprintf("Forbidden: Authentication denied for streamID url=%s err=%q", url.String(), err)) - return nil, errForbidden + clog.Errorf(ctx, "Authentication denied for streamID url=%s err=%q", url.String(), err) + return nil } // If we've received auth in header AND callback URL forms then for now, we reject cases where they're @@ -278,7 +272,7 @@ func createRTMPStreamIDHandler(_ctx context.Context, s *LivepeerServer, webhookR if resp != nil && webhookResponseOverride != nil { if !resp.areProfilesEqual(*webhookResponseOverride) { clog.Errorf(ctx, "Received auth header with profiles that don't match those in callback URL response") - return nil, fmt.Errorf("Received auth header with profiles that don't match those in callback URL response") + return nil } } @@ -300,9 +294,8 @@ func createRTMPStreamIDHandler(_ctx context.Context, s *LivepeerServer, webhookR parsedProfiles, err := ffmpeg.ParseProfilesFromJsonProfileArray(resp.Profiles) if err != nil { - errMsg := fmt.Sprintf("Failed to parse JSON video profile for streamID url=%s err=%q", url.String(), err) - clog.Errorf(ctx, errMsg) - return nil, fmt.Errorf(errMsg) + clog.Errorf(ctx, "Failed to parse JSON video profile for streamID url=%s err=%q", url.String(), err) + return nil } profiles = append(profiles, parsedProfiles...) @@ -315,18 +308,16 @@ func createRTMPStreamIDHandler(_ctx context.Context, s *LivepeerServer, webhookR if resp.ObjectStore != "" { os, err = drivers.ParseOSURL(resp.ObjectStore, false) if err != nil { - errMsg := fmt.Sprintf("Failed to parse object store url for streamID url=%s err=%q", url.String(), err) - clog.Errorf(ctx, errMsg) - return nil, fmt.Errorf(errMsg) + clog.Errorf(ctx, "Failed to parse object store url for streamID url=%s err=%q", url.String(), err) + return nil } } // set Recording OS if it was provided if resp.RecordObjectStore != "" { ros, err = drivers.ParseOSURL(resp.RecordObjectStore, true) if err != nil { - errMsg := fmt.Sprintf("Failed to parse recording object store url for streamID url=%s err=%q", url.String(), err) - clog.Errorf(ctx, errMsg) - return nil, fmt.Errorf(errMsg) + clog.Errorf(ctx, "Failed to parse recording object store url for streamID url=%s err=%q", url.String(), err) + return nil } } @@ -362,10 +353,8 @@ func createRTMPStreamIDHandler(_ctx context.Context, s *LivepeerServer, webhookR s.connectionLock.RLock() defer s.connectionLock.RUnlock() if core.MaxSessions > 0 && len(s.rtmpConnections) >= core.MaxSessions { - errMsg := fmt.Sprintf("Too many connections for streamID url=%s err=%q", url.String(), err) - clog.Errorf(ctx, errMsg) - return nil, fmt.Errorf(errMsg) - + clog.Errorf(ctx, "Too many connections for streamID url=%s err=%q", url.String(), err) + return nil } return &core.StreamParameters{ ManifestID: mid, @@ -378,7 +367,7 @@ func createRTMPStreamIDHandler(_ctx context.Context, s *LivepeerServer, webhookR RecordOS: ross, VerificationFreq: VerificationFreq, Nonce: nonce, - }, nil + } } } @@ -451,7 +440,6 @@ func gotRTMPStreamHandler(s *LivepeerServer) func(url *url.URL, rtmpStrm stream. func endRTMPStreamHandler(s *LivepeerServer) func(url *url.URL, rtmpStrm stream.RTMPVideoStream) error { return func(url *url.URL, rtmpStrm stream.RTMPVideoStream) error { params := streamParams(rtmpStrm.AppData()) - params.Capabilities.SetMinVersionConstraint(s.LivepeerNode.Capabilities.MinVersionConstraint()) if params == nil { return errMismatchedParams } @@ -838,15 +826,10 @@ func (s *LivepeerServer) HandlePush(w http.ResponseWriter, r *http.Request) { // Check for presence and register if a fresh cxn if !exists { - appData, err := (createRTMPStreamIDHandler(ctx, s, authHeaderConfig))(r.URL) - if err != nil { - if errors.Is(err, errForbidden) { - errorOut(http.StatusForbidden, "Could not create stream ID: url=%s", r.URL) - return - } else { - errorOut(http.StatusInternalServerError, "Could not create stream ID: url=%s", r.URL) - return - } + appData := (createRTMPStreamIDHandler(ctx, s, authHeaderConfig))(r.URL) + if appData == nil { + errorOut(http.StatusInternalServerError, "Could not create stream ID: url=%s", r.URL) + return } params := streamParams(appData) if authHeaderConfig != nil { diff --git a/server/mediaserver_test.go b/server/mediaserver_test.go index 595c4e29a..79b4f611f 100644 --- a/server/mediaserver_test.go +++ b/server/mediaserver_test.go @@ -438,9 +438,7 @@ func TestCreateRTMPStreamHandlerCap(t *testing.T) { oldMaxSessions := core.MaxSessions core.MaxSessions = 1 // happy case - id, err := createSid(u) - require.NoError(t, err) - sid := id.(*core.StreamParameters) + sid := createSid(u).(*core.StreamParameters) mid := sid.ManifestID if mid != "id1" { t.Error("Stream should be allowd", sid) @@ -450,8 +448,7 @@ func TestCreateRTMPStreamHandlerCap(t *testing.T) { } s.rtmpConnections[core.ManifestID("id1")] = nil // capped case - params, err := createSid(u) - require.Error(t, err) + params := createSid(u) if params != nil { t.Error("Stream should be denied because of capacity cap") } @@ -463,7 +460,7 @@ type authWebhookReq struct { } func TestCreateRTMPStreamHandlerWebhook(t *testing.T) { - assert := require.New(t) + assert := assert.New(t) s, cancel := setupServerWithCancel() defer serverCleanup(s) defer cancel() @@ -472,8 +469,7 @@ func TestCreateRTMPStreamHandlerWebhook(t *testing.T) { AuthWebhookURL = mustParseUrl(t, "http://localhost:8938/notexisting") u := mustParseUrl(t, "http://hot/something/id1") - sid, err := createSid(u) - assert.Error(err) + sid := createSid(u) assert.Nil(sid, "Webhook auth failed") ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -490,8 +486,7 @@ func TestCreateRTMPStreamHandlerWebhook(t *testing.T) { })) defer ts.Close() AuthWebhookURL = mustParseUrl(t, ts.URL) - sid, err = createSid(u) - assert.NoError(err) + sid = createSid(u) assert.NotNil(sid, "On empty response with 200 code should pass") // local helper to reduce boilerplate @@ -508,23 +503,19 @@ func TestCreateRTMPStreamHandlerWebhook(t *testing.T) { // empty manifestID ts2 := makeServer(`{"manifestID":""}`) defer ts2.Close() - sid, err = createSid(u) - assert.Error(err) + sid = createSid(u) assert.Nil(sid, "Should not pass if returned manifest id is empty") // invalid json ts3 := makeServer(`{manifestID:"XX"}`) defer ts3.Close() - sid, err = createSid(u) - assert.Error(err) + sid = createSid(u) assert.Nil(sid, "Should not pass if returned json is invalid") // set manifestID ts4 := makeServer(`{"manifestID":"xy"}`) defer ts4.Close() - p, err := createSid(u) - assert.NoError(err) - params := p.(*core.StreamParameters) + params := createSid(u).(*core.StreamParameters) mid := params.ManifestID assert.Equal(core.ManifestID("xy"), mid, "Should set manifest id to one provided by webhook") @@ -535,9 +526,7 @@ func TestCreateRTMPStreamHandlerWebhook(t *testing.T) { // set manifestID + streamKey ts5 := makeServer(`{"manifestID":"xyz", "streamKey":"zyx"}`) defer ts5.Close() - id, err := createSid(u) - require.NoError(t, err) - params = id.(*core.StreamParameters) + params = createSid(u).(*core.StreamParameters) mid = params.ManifestID assert.Equal(core.ManifestID("xyz"), mid, "Should set manifest to one provided by webhook") assert.Equal("xyz/zyx", params.StreamID(), "Should set streamkey to one provided by webhook") @@ -546,9 +535,7 @@ func TestCreateRTMPStreamHandlerWebhook(t *testing.T) { // set presets (with some invalid) ts6 := makeServer(`{"manifestID":"a", "presets":["P240p30fps16x9", "unknown", "P720p30fps16x9"]}`) defer ts6.Close() - strmID, err := createSid(u) - require.NoError(t, err) - params = strmID.(*core.StreamParameters) + params = createSid(u).(*core.StreamParameters) assert.Len(params.Profiles, 2) assert.Equal(params.Profiles, []ffmpeg.VideoProfile{ffmpeg.P240p30fps16x9, ffmpeg.P720p30fps16x9}, "Did not have matching presets") @@ -560,9 +547,7 @@ func TestCreateRTMPStreamHandlerWebhook(t *testing.T) { {"name": "passthru_fps", "bitrate": 890, "width": 789, "height": 654, "profile": "H264ConstrainedHigh", "gop":"123"}, {"name": "gop0", "bitrate": 800, "width": 400, "height": 220, "profile": "H264ConstrainedHigh", "gop":"0.0"}]}`) defer ts7.Close() - data, err := createSid(u) - require.NoError(t, err) - params = data.(*core.StreamParameters) + params = createSid(u).(*core.StreamParameters) assert.Len(params.Profiles, 4) expectedProfiles := []ffmpeg.VideoProfile{ @@ -612,9 +597,7 @@ func TestCreateRTMPStreamHandlerWebhook(t *testing.T) { {"name": "prof1", "bitrate": 432, "fps": 560, "width": 123, "height": 456}, {"name": "prof2", "bitrate": 765, "fps": 876, "width": 456, "height": "hello"}]}`) defer ts8.Close() - appData, err := createSid(u) - require.Error(t, err) - params, ok := appData.(*core.StreamParameters) + params, ok := createSid(u).(*core.StreamParameters) assert.False(ok) assert.Nil(params) @@ -626,9 +609,7 @@ func TestCreateRTMPStreamHandlerWebhook(t *testing.T) { {"name": "gop0", "bitrate": 800, "width": 400, "height": 220, "profile": "H264ConstrainedHigh", "gop":"0.0"}]}`) defer ts9.Close() - i, err := createSid(u) - require.NoError(t, err) - params = i.(*core.StreamParameters) + params = createSid(u).(*core.StreamParameters) jointProfiles := append([]ffmpeg.VideoProfile{ffmpeg.P240p30fps16x9, ffmpeg.P720p30fps16x9}, expectedProfiles...) assert.Len(params.Profiles, 6) @@ -637,9 +618,7 @@ func TestCreateRTMPStreamHandlerWebhook(t *testing.T) { // all invalid presets in webhook should lead to empty set ts10 := makeServer(`{"manifestID":"a", "presets":["very", "unknown"]}`) defer ts10.Close() - id2, err := createSid(u) - require.NoError(t, err) - params = id2.(*core.StreamParameters) + params = createSid(u).(*core.StreamParameters) assert.Len(params.Profiles, 0, "Unexpected value in presets") // invalid gops @@ -657,31 +636,25 @@ func TestCreateRTMPStreamHandlerWebhook(t *testing.T) { // intra only gop ts14 := makeServer(`{"manifestID":"a", "profiles": [ {"gop": "intra" }]}`) defer ts14.Close() - id3, err := createSid(u) - require.NoError(t, err) - params = id3.(*core.StreamParameters) + params = createSid(u).(*core.StreamParameters) assert.Len(params.Profiles, 1) assert.Equal(ffmpeg.GOPIntraOnly, params.Profiles[0].GOP) // do not create stream if ObjectStore URL is invalid ts15 := makeServer(`{"manifestID":"a2", "objectStore": "invalid://object.store", "recordObjectStore": ""}`) defer ts15.Close() - sid, err = createSid(u) - require.Error(t, err) + sid = createSid(u) assert.Nil(sid) // do not create stream if RecordObjectStore URL is invalid ts16 := makeServer(`{"manifestID":"a2", "objectStore": "", "recordObjectStore": "invalid://object.store"}`) defer ts16.Close() - sid, err = createSid(u) - require.Error(t, err) + sid = createSid(u) assert.Nil(sid) ts17 := makeServer(`{"manifestID":"a3", "objectStore": "s3+http://us:pass@object.store/path", "recordObjectStore": "s3+http://us:pass@record.store"}`) defer ts17.Close() - id4, err := createSid(u) - require.NoError(t, err) - params = id4.(*core.StreamParameters) + params = createSid(u).(*core.StreamParameters) assert.Equal(core.ManifestID("a3"), params.ManifestID) assert.NotNil(params.OS) assert.True(params.OS.IsExternal()) @@ -716,41 +689,30 @@ func TestCreateRTMPStreamHandler(t *testing.T) { u := mustParseUrl(t, "rtmp://localhost/"+expectedSid.String()) // with key rand.Seed(123) - sid, err := createSid(u) - require.NoError(t, err) + sid := createSid(u) sap := sid.(*core.StreamParameters) assert.Equal(t, uint64(0x4a68998bed5c40f1), sap.Nonce) - sid, err = createSid(u) - require.NoError(t, err) - if sid.StreamID() != expectedSid.String() { + if sid := createSid(u); sid.StreamID() != expectedSid.String() { t.Error("Unexpected streamid", sid.StreamID()) } u = mustParseUrl(t, "rtmp://localhost/stream/"+expectedSid.String()) // with stream - sid, err = createSid(u) - require.NoError(t, err) - if sid.StreamID() != expectedSid.String() { + if sid := createSid(u); sid.StreamID() != expectedSid.String() { t.Error("Unexpected streamid") } expectedMid := "mnopq" key := common.RandomIDGenerator(StreamKeyBytes) u = mustParseUrl(t, "rtmp://localhost/"+string(expectedMid)) // without key - sid, err = createSid(u) - require.NoError(t, err) - if sid.StreamID() != string(expectedMid)+"/"+key { + if sid := createSid(u); sid.StreamID() != string(expectedMid)+"/"+key { t.Error("Unexpected streamid", sid.StreamID()) } u = mustParseUrl(t, "rtmp://localhost/stream/"+string(expectedMid)) // with stream, without key - sid, err = createSid(u) - require.NoError(t, err) - if sid.StreamID() != string(expectedMid)+"/"+key { + if sid := createSid(u); sid.StreamID() != string(expectedMid)+"/"+key { t.Error("Unexpected streamid", sid.StreamID()) } // Test normal case u = mustParseUrl(t, "rtmp://localhost") - id, err := createSid(u) - require.NoError(t, err) - st := stream.NewBasicRTMPVideoStream(id) + st := stream.NewBasicRTMPVideoStream(createSid(u)) if st.GetStreamID() == "" { t.Error("Empty streamid") } @@ -759,18 +721,14 @@ func TestCreateRTMPStreamHandler(t *testing.T) { t.Error("Handler failed ", err) } // Test collisions via stream reuse - sid, err = createSid(u) - require.NoError(t, err) - if sid == nil { + if sid := createSid(u); sid == nil { t.Error("Did not expect a failure due to naming collision") } // Ensure the stream ID is reusable after the stream ends if err := endHandler(u, st); err != nil { t.Error("Could not clean up stream") } - sid, err = createSid(u) - require.NoError(t, err) - if sid.StreamID() != st.GetStreamID() { + if sid := createSid(u); sid.StreamID() != st.GetStreamID() { t.Error("Mismatched streamid during stream reuse", sid.StreamID(), st.GetStreamID()) } @@ -781,9 +739,7 @@ func TestCreateRTMPStreamHandler(t *testing.T) { // This isn't a great test because if the query param ever changes, // this test will still pass u := mustParseUrl(t, "rtmp://localhost/"+inp) - sid, err = createSid(u) - require.NoError(t, err) - if sid.StreamID() != st.GetStreamID() { + if sid := createSid(u); sid.StreamID() != st.GetStreamID() { t.Errorf("Unexpected StreamID for '%v' ; expected '%v' for input '%v'", sid, st.GetStreamID(), inp) } } @@ -848,8 +804,7 @@ func TestCreateRTMPStreamHandlerWithAuthHeader(t *testing.T) { expectedSid := core.MakeStreamIDFromString("override-manifest-id", "abcdef") u := mustParseUrl(t, "rtmp://localhost/"+expectedSid.String()) // with key - sid, err := createSid(u) - require.NoError(t, err) + sid := createSid(u) require.NotNil(t, sid) require.Equal(t, expectedSid.String(), sid.StreamID()) @@ -919,8 +874,7 @@ func TestCreateRTMPStreamHandlerWithAuthHeader_DifferentProfilesToCallbackURL(t expectedSid := core.MakeStreamIDFromString("override-manifest-id", "abcdef") u := mustParseUrl(t, "rtmp://localhost/"+expectedSid.String()) // with key - sid, err := createSid(u) - require.Error(t, err) + sid := createSid(u) require.Nil(t, sid) } @@ -933,8 +887,7 @@ func TestEndRTMPStreamHandler(t *testing.T) { handler := gotRTMPStreamHandler(s) endHandler := endRTMPStreamHandler(s) u := mustParseUrl(t, "rtmp://localhost") - sid, err := createSid(u) - require.NoError(t, err) + sid := createSid(u) st := stream.NewBasicRTMPVideoStream(sid) // Nonexistent stream @@ -1045,9 +998,7 @@ func TestMultiStream(t *testing.T) { createSid := createRTMPStreamIDHandler(context.TODO(), s, nil) handleStream := func(i int) { - id, err := createSid(u) - require.NoError(t, err) - st := stream.NewBasicRTMPVideoStream(id) + st := stream.NewBasicRTMPVideoStream(createSid(u)) if err := handler(u, st); err != nil { t.Error("Could not handle stream ", i, err) } @@ -1278,8 +1229,7 @@ func TestBroadcastSessionManagerWithStreamStartStop(t *testing.T) { // create BasicRTMPVideoStream and extract ManifestID u := mustParseUrl(t, "rtmp://localhost") - sid, err := createSid(u) - assert.NoError(err) + sid := createSid(u) st := stream.NewBasicRTMPVideoStream(sid) mid := streamParams(st.AppData()).ManifestID @@ -1288,8 +1238,8 @@ func TestBroadcastSessionManagerWithStreamStartStop(t *testing.T) { assert.Equal(exists, false) // assert stream starts successfully - err = handler(u, st) - assert.NoError(err) + err := handler(u, st) + assert.Nil(err) // assert sessManager is running and has right number of sessions cxn, exists := s.rtmpConnections[mid] diff --git a/server/push_test.go b/server/push_test.go index bb17ac8a1..b5d0817e0 100644 --- a/server/push_test.go +++ b/server/push_test.go @@ -1037,7 +1037,7 @@ func TestPush_ForAuthWebhookFailure(t *testing.T) { body, err := ioutil.ReadAll(resp.Body) require.Nil(t, err) - assert.Equal(http.StatusForbidden, resp.StatusCode) + assert.Equal(http.StatusInternalServerError, resp.StatusCode) assert.Contains(strings.TrimSpace(string(body)), "Could not create stream ID") } diff --git a/server/rpc.go b/server/rpc.go index 0c8b9f806..0fc46494f 100644 --- a/server/rpc.go +++ b/server/rpc.go @@ -280,7 +280,7 @@ func GetOrchestratorInfo(ctx context.Context, bcast common.Broadcaster, orchestr return r, nil } -// EndTranscodingSession - the broadcaster calls EndTranscodingSession to tear down sessions used for verification only once +// EndSession - the broadcaster calls EndTranscodingSession to tear down sessions used for verification only once func EndTranscodingSession(ctx context.Context, sess *BroadcastSession) error { uri, err := url.Parse(sess.Transcoder()) if err != nil { @@ -338,6 +338,7 @@ func getOrchestrator(orch Orchestrator, req *net.OrchestratorRequest) (*net.Orch } // currently, orchestrator == transcoder + if req.Capabilities == nil { return orchestratorInfo(orch, addr, orch.ServiceURI().String(), "") } diff --git a/server/rpc_test.go b/server/rpc_test.go index c0832a0c4..712568c20 100644 --- a/server/rpc_test.go +++ b/server/rpc_test.go @@ -549,13 +549,13 @@ func TestGenPayment(t *testing.T) { sender := &pm.MockSender{} s.Sender = sender - // Test changing O price - s.InitialPrice = &net.PriceInfo{PricePerUnit: 1, PixelsPerUnit: 7} + // Test invalid price + BroadcastCfg.SetMaxPrice(big.NewRat(1, 5)) payment, err = genPayment(context.TODO(), s, 1) assert.Equal("", payment) - assert.Errorf(err, "Orchestrator price has more than doubled, Orchestrator price: %v, Orchestrator initial price: %v", "1/3", "1/7") + assert.Errorf(err, err.Error(), "Orchestrator price higher than the set maximum price of %v wei per %v pixels", int64(1), int64(5)) - s.InitialPrice = nil + BroadcastCfg.SetMaxPrice(nil) // Test CreateTicketBatch error sender.On("CreateTicketBatch", mock.Anything, mock.Anything).Return(nil, errors.New("CreateTicketBatch error")).Once() @@ -680,10 +680,22 @@ func TestValidatePrice(t *testing.T) { PMSessionID: "foo", } - // O's Initial Price is nil + // B's MaxPrice is nil err := validatePrice(s) assert.Nil(err) + defer BroadcastCfg.SetMaxPrice(nil) + + // B MaxPrice > O Price + BroadcastCfg.SetMaxPrice(big.NewRat(5, 1)) + err = validatePrice(s) + assert.Nil(err) + + // B MaxPrice == O Price + BroadcastCfg.SetMaxPrice(big.NewRat(1, 3)) + err = validatePrice(s) + assert.Nil(err) + // O Initial Price == O Price s.InitialPrice = oinfo.PriceInfo err = validatePrice(s) @@ -694,15 +706,16 @@ func TestValidatePrice(t *testing.T) { err = validatePrice(s) assert.Nil(err) - // O Price higher but up to 2x Initial Price - s.InitialPrice = &net.PriceInfo{PricePerUnit: 1, PixelsPerUnit: 6} + // O Initial Price lower than O Price + s.InitialPrice = &net.PriceInfo{PricePerUnit: 1, PixelsPerUnit: 10} err = validatePrice(s) - assert.Nil(err) + assert.ErrorContains(err, "price has changed") - // O Price higher than 2x Initial Price - s.InitialPrice = &net.PriceInfo{PricePerUnit: 1000, PixelsPerUnit: 6001} + // B MaxPrice < O Price + s.InitialPrice = nil + BroadcastCfg.SetMaxPrice(big.NewRat(1, 5)) err = validatePrice(s) - assert.ErrorContains(err, "price has more than doubled") + assert.EqualError(err, fmt.Sprintf("Orchestrator price higher than the set maximum price of %v wei per %v pixels", int64(1), int64(5))) // O.PriceInfo is nil s.OrchestratorInfo.PriceInfo = nil diff --git a/server/segment_rpc.go b/server/segment_rpc.go index b7a948b5f..7f81999f7 100644 --- a/server/segment_rpc.go +++ b/server/segment_rpc.go @@ -36,9 +36,6 @@ const segmentHeader = "Livepeer-Segment" const pixelEstimateMultiplier = 1.02 -// Maximum price change allowed in orchestrator pricing before the session is swapped. -var priceIncreaseThreshold = big.NewRat(2, 1) - var errSegEncoding = errors.New("ErrorSegEncoding") var errSegSig = errors.New("ErrSegSig") var errFormat = errors.New("unrecognized profile output format") @@ -829,18 +826,13 @@ func validatePrice(sess *BroadcastSession) error { return errors.New("missing orchestrator price") } - initPrice, err := common.RatPriceInfo(sess.InitialPrice) - if err != nil { - glog.Warningf("Error parsing session initial price (%d / %d): %v", - sess.InitialPrice.PricePerUnit, sess.InitialPrice.PixelsPerUnit, err) + maxPrice := BroadcastCfg.MaxPrice() + if maxPrice != nil && oPrice.Cmp(maxPrice) == 1 { + return fmt.Errorf("Orchestrator price higher than the set maximum price of %v wei per %v pixels", maxPrice.Num().Int64(), maxPrice.Denom().Int64()) } - if initPrice != nil { - // Prices are dynamic if configured with a custom currency, so we need to allow some change during the session. - // TODO: Make sure prices stay the same during a session so we can make this logic more strict, disallowing any price changes. - maxIncreasedPrice := new(big.Rat).Mul(initPrice, priceIncreaseThreshold) - if oPrice.Cmp(maxIncreasedPrice) > 0 { - return fmt.Errorf("Orchestrator price has more than doubled, Orchestrator price: %v, Orchestrator initial price: %v", oPrice.RatString(), initPrice.RatString()) - } + iPrice, err := common.RatPriceInfo(sess.InitialPrice) + if err == nil && iPrice != nil && oPrice.Cmp(iPrice) == 1 { + return fmt.Errorf("Orchestrator price has changed, Orchestrator price: %v, Orchestrator initial price: %v", oPrice, iPrice) } return nil diff --git a/server/segment_rpc_test.go b/server/segment_rpc_test.go index 282e3187d..6feb1a5ba 100644 --- a/server/segment_rpc_test.go +++ b/server/segment_rpc_test.go @@ -1677,15 +1677,14 @@ func TestSubmitSegment_GenPaymentError_ValidatePriceError(t *testing.T) { Sender: sender, Balance: balance, OrchestratorInfo: oinfo, - InitialPrice: &net.PriceInfo{ - PricePerUnit: 1, - PixelsPerUnit: 7, - }, } + BroadcastCfg.SetMaxPrice(big.NewRat(1, 5)) + defer BroadcastCfg.SetMaxPrice(nil) + _, err := SubmitSegment(context.TODO(), s, &stream.HLSSegment{}, nil, 0, false, true) - assert.EqualError(t, err, fmt.Sprintf("Orchestrator price has more than doubled, Orchestrator price: %v, Orchestrator initial price: %v", "1/3", "1/7")) + assert.EqualErrorf(t, err, err.Error(), "Orchestrator price higher than the set maximum price of %v wei per %v pixels", int64(1), int64(5)) balance.AssertCalled(t, "Credit", existingCredit) } diff --git a/server/selection.go b/server/selection.go index c827034db..c7187c79e 100644 --- a/server/selection.go +++ b/server/selection.go @@ -3,8 +3,6 @@ package server import ( "container/heap" "context" - "math/big" - ethcommon "github.com/ethereum/go-ethereum/common" "github.com/livepeer/go-livepeer/clog" "github.com/livepeer/go-livepeer/common" @@ -169,7 +167,7 @@ func (s *MinLSSelector) selectUnknownSession(ctx context.Context) *BroadcastSess } var addrs []ethcommon.Address - prices := map[ethcommon.Address]*big.Rat{} + prices := map[ethcommon.Address]float64{} addrCount := make(map[ethcommon.Address]int) for _, sess := range s.unknownSessions { if sess.OrchestratorInfo.GetTicketParams() == nil { @@ -182,10 +180,9 @@ func (s *MinLSSelector) selectUnknownSession(ctx context.Context) *BroadcastSess addrCount[addr]++ pi := sess.OrchestratorInfo.PriceInfo if pi != nil && pi.PixelsPerUnit != 0 { - prices[addr] = big.NewRat(pi.PricePerUnit, pi.PixelsPerUnit) + prices[addr] = float64(pi.PricePerUnit) / float64(pi.PixelsPerUnit) } } - maxPrice := BroadcastCfg.MaxPrice() stakes, err := s.stakeRdr.Stakes(addrs) if err != nil { @@ -202,7 +199,7 @@ func (s *MinLSSelector) selectUnknownSession(ctx context.Context) *BroadcastSess s.perfScore.Mu.Unlock() } - selected := s.selectionAlgorithm.Select(ctx, addrs, stakes, maxPrice, prices, perfScores) + selected := s.selectionAlgorithm.Select(addrs, stakes, prices, perfScores) for i, sess := range s.unknownSessions { if sess.OrchestratorInfo.GetTicketParams() == nil { diff --git a/server/selection_algorithm.go b/server/selection_algorithm.go index 050b45928..72bc2a663 100644 --- a/server/selection_algorithm.go +++ b/server/selection_algorithm.go @@ -1,14 +1,11 @@ package server import ( - "context" + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/golang/glog" "math" - "math/big" "math/rand" "time" - - ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/livepeer/go-livepeer/clog" ) var random = rand.New(rand.NewSource(time.Now().UnixNano())) @@ -23,19 +20,14 @@ type ProbabilitySelectionAlgorithm struct { PriceExpFactor float64 } -func (sa ProbabilitySelectionAlgorithm) Select(ctx context.Context, addrs []ethcommon.Address, stakes map[ethcommon.Address]int64, maxPrice *big.Rat, prices map[ethcommon.Address]*big.Rat, perfScores map[ethcommon.Address]float64) ethcommon.Address { - filtered := sa.filter(ctx, addrs, maxPrice, prices, perfScores) +func (sa ProbabilitySelectionAlgorithm) Select(addrs []ethcommon.Address, stakes map[ethcommon.Address]int64, prices map[ethcommon.Address]float64, perfScores map[ethcommon.Address]float64) ethcommon.Address { + filtered := sa.filter(addrs, perfScores) probabilities := sa.calculateProbabilities(filtered, stakes, prices) return selectBy(probabilities) } -func (sa ProbabilitySelectionAlgorithm) filter(ctx context.Context, addrs []ethcommon.Address, maxPrice *big.Rat, prices map[ethcommon.Address]*big.Rat, perfScores map[ethcommon.Address]float64) []ethcommon.Address { - filteredByPerfScore := sa.filterByPerfScore(ctx, addrs, perfScores) - return sa.filterByMaxPrice(ctx, filteredByPerfScore, maxPrice, prices) -} - -func (sa ProbabilitySelectionAlgorithm) filterByPerfScore(ctx context.Context, addrs []ethcommon.Address, scores map[ethcommon.Address]float64) []ethcommon.Address { - if sa.MinPerfScore <= 0 || len(scores) == 0 { +func (sa ProbabilitySelectionAlgorithm) filter(addrs []ethcommon.Address, scores map[ethcommon.Address]float64) []ethcommon.Address { + if sa.MinPerfScore <= 0 || scores == nil || len(scores) == 0 { // Performance Score filter not defined, return all Orchestrators return addrs } @@ -48,41 +40,18 @@ func (sa ProbabilitySelectionAlgorithm) filterByPerfScore(ctx context.Context, a } if len(res) == 0 { - // If no orchestrators pass the perf filter, return all Orchestrators. + // If no orchestrators pass the filter, then returns all Orchestrators // That may mean some issues with the PerfScore service. - clog.Warningf(ctx, "No Orchestrators passed min performance score filter, not using the filter, numAddrs=%d, minPerfScore=%v, scores=%v, addrs=%v", len(addrs), sa.MinPerfScore, scores, addrs) - return addrs - } - return res -} - -func (sa ProbabilitySelectionAlgorithm) filterByMaxPrice(ctx context.Context, addrs []ethcommon.Address, maxPrice *big.Rat, prices map[ethcommon.Address]*big.Rat) []ethcommon.Address { - if maxPrice == nil || len(prices) == 0 { - // Max price filter not defined, return all Orchestrators - return addrs - } - - var res []ethcommon.Address - for _, addr := range addrs { - price := prices[addr] - if price != nil && price.Cmp(maxPrice) <= 0 { - res = append(res, addr) - } - } - - if len(res) == 0 { - // If no orchestrators pass the filter, return all Orchestrators - // It means that no orchestrators are below the max price - clog.Warningf(ctx, "No Orchestrators passed max price filter, not using the filter, numAddrs=%d, maxPrice=%v, prices=%v, addrs=%v", len(addrs), maxPrice, prices, addrs) + glog.Warning("No Orchestrators passed min performance score filter, not using the filter") return addrs } return res } -func (sa ProbabilitySelectionAlgorithm) calculateProbabilities(addrs []ethcommon.Address, stakes map[ethcommon.Address]int64, prices map[ethcommon.Address]*big.Rat) map[ethcommon.Address]float64 { +func (sa ProbabilitySelectionAlgorithm) calculateProbabilities(addrs []ethcommon.Address, stakes map[ethcommon.Address]int64, prices map[ethcommon.Address]float64) map[ethcommon.Address]float64 { pricesNorm := map[ethcommon.Address]float64{} for _, addr := range addrs { - p, _ := prices[addr].Float64() + p := prices[addr] pricesNorm[addr] = math.Exp(-1 * p / sa.PriceExpFactor) } diff --git a/server/selection_algorithm_test.go b/server/selection_algorithm_test.go index ffae12de3..96ef58f0a 100644 --- a/server/selection_algorithm_test.go +++ b/server/selection_algorithm_test.go @@ -1,12 +1,9 @@ package server import ( - "context" - "math/big" - "testing" - ethcommon "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" + "testing" ) const testPriceExpFactor = 100 @@ -15,8 +12,6 @@ func TestFilter(t *testing.T) { tests := []struct { name string orchMinPerfScore float64 - maxPrice float64 - prices map[string]float64 orchPerfScores map[string]float64 orchestrators []string want []string @@ -90,126 +85,21 @@ func TestFilter(t *testing.T) { "0x0000000000000000000000000000000000000004", }, }, - { - name: "All prices below max price", - orchMinPerfScore: 0.7, - maxPrice: 2000, - prices: map[string]float64{ - "0x0000000000000000000000000000000000000001": 500, - "0x0000000000000000000000000000000000000002": 1500, - "0x0000000000000000000000000000000000000003": 1000, - }, - orchPerfScores: map[string]float64{ - "0x0000000000000000000000000000000000000001": 0.6, - "0x0000000000000000000000000000000000000002": 0.8, - "0x0000000000000000000000000000000000000003": 0.9, - }, - orchestrators: []string{ - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000003", - }, - want: []string{ - "0x0000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000003", - }, - }, - { - name: "All prices above max price", - orchMinPerfScore: 0.7, - maxPrice: 100, - prices: map[string]float64{ - "0x0000000000000000000000000000000000000001": 500, - "0x0000000000000000000000000000000000000002": 1500, - "0x0000000000000000000000000000000000000003": 1000, - }, - orchPerfScores: map[string]float64{ - "0x0000000000000000000000000000000000000001": 0.6, - "0x0000000000000000000000000000000000000002": 0.8, - "0x0000000000000000000000000000000000000003": 0.9, - }, - orchestrators: []string{ - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000003", - }, - want: []string{ - "0x0000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000003", - }, - }, - { - name: "Mix of prices relative to max price", - orchMinPerfScore: 0.7, - maxPrice: 750, - prices: map[string]float64{ - "0x0000000000000000000000000000000000000001": 500, - "0x0000000000000000000000000000000000000002": 1500, - "0x0000000000000000000000000000000000000003": 700, - }, - orchPerfScores: map[string]float64{ - "0x0000000000000000000000000000000000000001": 0.8, - "0x0000000000000000000000000000000000000002": 0.6, - "0x0000000000000000000000000000000000000003": 0.9, - }, - orchestrators: []string{ - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000003", - }, - want: []string{ - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000003", - }, - }, - { - name: "Exact match with max price", - orchMinPerfScore: 0.7, - maxPrice: 1000, - prices: map[string]float64{ - "0x0000000000000000000000000000000000000001": 500, - "0x0000000000000000000000000000000000000002": 1000, // exactly max - "0x0000000000000000000000000000000000000003": 1500, - }, - orchPerfScores: map[string]float64{ - "0x0000000000000000000000000000000000000001": 0.8, - "0x0000000000000000000000000000000000000002": 0.9, - "0x0000000000000000000000000000000000000003": 0.6, - }, - orchestrators: []string{ - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000003", - }, - want: []string{ - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000002", - }, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var addrs []ethcommon.Address - var maxPrice *big.Rat - prices := map[ethcommon.Address]*big.Rat{} perfScores := map[ethcommon.Address]float64{} for _, o := range tt.orchestrators { - addr := ethcommon.HexToAddress(o) - addrs = append(addrs, addr) - perfScores[addr] = tt.orchPerfScores[o] - if price, ok := tt.prices[o]; ok { - prices[addr] = new(big.Rat).SetFloat64(price) - } - } - if tt.maxPrice > 0 { - maxPrice = new(big.Rat).SetFloat64(tt.maxPrice) + perfScores[ethcommon.HexToAddress(o)] = tt.orchPerfScores[o] + addrs = append(addrs, ethcommon.HexToAddress(o)) } sa := &ProbabilitySelectionAlgorithm{ MinPerfScore: tt.orchMinPerfScore, } - res := sa.filter(context.Background(), addrs, maxPrice, prices, perfScores) + res := sa.filter(addrs, perfScores) var exp []ethcommon.Address for _, o := range tt.want { @@ -270,13 +160,13 @@ func TestCalculateProbabilities(t *testing.T) { t.Run(tt.name, func(t *testing.T) { var orchs []ethcommon.Address stakes := map[ethcommon.Address]int64{} - prices := map[ethcommon.Address]*big.Rat{} + prices := map[ethcommon.Address]float64{} expProbs := map[ethcommon.Address]float64{} for i, addrStr := range tt.addrs { addr := ethcommon.HexToAddress(addrStr) orchs = append(orchs, addr) stakes[addr] = tt.stakes[i] - prices[addr] = new(big.Rat).SetFloat64(tt.prices[i]) + prices[addr] = tt.prices[i] expProbs[addr] = tt.want[i] } diff --git a/server/selection_test.go b/server/selection_test.go index aa936ddb0..716c780d0 100644 --- a/server/selection_test.go +++ b/server/selection_test.go @@ -4,12 +4,10 @@ import ( "container/heap" "context" "errors" - "math/big" - "testing" - "github.com/livepeer/go-livepeer/core" "github.com/livepeer/go-livepeer/net" "github.com/stretchr/testify/require" + "testing" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/livepeer/go-livepeer/common" @@ -91,7 +89,7 @@ func (r *stubStakeReader) SetStakes(stakes map[ethcommon.Address]int64) { type stubSelectionAlgorithm struct{} -func (sa stubSelectionAlgorithm) Select(ctx context.Context, addrs []ethcommon.Address, stakes map[ethcommon.Address]int64, maxPrice *big.Rat, prices map[ethcommon.Address]*big.Rat, perfScores map[ethcommon.Address]float64) ethcommon.Address { +func (sa stubSelectionAlgorithm) Select(addrs []ethcommon.Address, stakes map[ethcommon.Address]int64, prices map[ethcommon.Address]float64, perfScores map[ethcommon.Address]float64) ethcommon.Address { if len(addrs) == 0 { return ethcommon.Address{} } @@ -100,7 +98,7 @@ func (sa stubSelectionAlgorithm) Select(ctx context.Context, addrs []ethcommon.A // select lowest price lowest := prices[addr] for _, a := range addrs { - if prices[a].Cmp(lowest) < 0 { + if prices[a] < lowest { addr = a lowest = prices[a] } diff --git a/test/e2e/e2e.go b/test/e2e/e2e.go index efc723d60..59a77b5c8 100644 --- a/test/e2e/e2e.go +++ b/test/e2e/e2e.go @@ -122,7 +122,7 @@ func lpCfg() starter.LivepeerConfig { ethPassword := "" network := "devnet" blockPollingInterval := 1 - pricePerUnit := "1" + pricePerUnit := 1 initializeRound := true cfg := starter.DefaultLivepeerConfig() diff --git a/tools.go b/tools.go deleted file mode 100644 index 6bc191ddd..000000000 --- a/tools.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build tools -// +build tools - -package tools - -import ( - _ "github.com/ethereum/go-ethereum/cmd/abigen" - _ "github.com/golang/mock/mockgen" -) From 350556d7b3633dd4d02d8964a3e3bb71a324290e Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Fri, 9 Aug 2024 23:42:51 +0200 Subject: [PATCH 162/203] refactor: improve rpc message (#3115) * refactor: unify capabilities constraints message structure This commit unifies the capabilities constraints message structure by combining the minVersion constraint and CapabilityConstraints into a single Constraints message field. The `NewCapabilitiesWithConstraints` method has been removed for consistency with how the minVersion constraint is handled, allowing the constraints fields to be private. * fix(ai): prevent nil error crash Ensure backward compatibility by handling cases where the Constraints field is missing in the capabilities request message. This prevents crashes when a Gateway with older software calls the updated orchestrator. --- cmd/livepeer/starter/starter.go | 15 +- core/capabilities.go | 71 ++++---- core/orchestrator.go | 30 ++-- net/lp_rpc.pb.go | 294 ++++++++++++++++---------------- net/lp_rpc.proto | 5 +- server/ai_session.go | 7 +- 6 files changed, 216 insertions(+), 206 deletions(-) diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index 5354820ab..a65c463ef 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -1061,7 +1061,7 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { } var aiCaps []core.Capability - capabilityConstraints := make(map[core.Capability]*core.PerCapabilityConstraints) + capabilityConstraints := make(core.PerCapabilityConstraints) if *cfg.AIWorker { gpus := []string{} @@ -1159,7 +1159,7 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { _, ok := capabilityConstraints[core.Capability_TextToImage] if !ok { aiCaps = append(aiCaps, core.Capability_TextToImage) - capabilityConstraints[core.Capability_TextToImage] = &core.PerCapabilityConstraints{ + capabilityConstraints[core.Capability_TextToImage] = &core.CapabilityConstraints{ Models: make(map[string]*core.ModelConstraint), } } @@ -1173,7 +1173,7 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { _, ok := capabilityConstraints[core.Capability_ImageToImage] if !ok { aiCaps = append(aiCaps, core.Capability_ImageToImage) - capabilityConstraints[core.Capability_ImageToImage] = &core.PerCapabilityConstraints{ + capabilityConstraints[core.Capability_ImageToImage] = &core.CapabilityConstraints{ Models: make(map[string]*core.ModelConstraint), } } @@ -1187,7 +1187,7 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { _, ok := capabilityConstraints[core.Capability_ImageToVideo] if !ok { aiCaps = append(aiCaps, core.Capability_ImageToVideo) - capabilityConstraints[core.Capability_ImageToVideo] = &core.PerCapabilityConstraints{ + capabilityConstraints[core.Capability_ImageToVideo] = &core.CapabilityConstraints{ Models: make(map[string]*core.ModelConstraint), } } @@ -1201,7 +1201,7 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { _, ok := capabilityConstraints[core.Capability_Upscale] if !ok { aiCaps = append(aiCaps, core.Capability_Upscale) - capabilityConstraints[core.Capability_Upscale] = &core.PerCapabilityConstraints{ + capabilityConstraints[core.Capability_Upscale] = &core.CapabilityConstraints{ Models: make(map[string]*core.ModelConstraint), } } @@ -1215,7 +1215,7 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { _, ok := capabilityConstraints[core.Capability_AudioToText] if !ok { aiCaps = append(aiCaps, core.Capability_AudioToText) - capabilityConstraints[core.Capability_AudioToText] = &core.PerCapabilityConstraints{ + capabilityConstraints[core.Capability_AudioToText] = &core.CapabilityConstraints{ Models: make(map[string]*core.ModelConstraint), } } @@ -1405,7 +1405,8 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { *cfg.CliAddr = defaultAddr(*cfg.CliAddr, "127.0.0.1", TranscoderCliPort) } - n.Capabilities = core.NewCapabilitiesWithConstraints(append(transcoderCaps, aiCaps...), core.MandatoryOCapabilities(), core.Constraints{}, capabilityConstraints) + n.Capabilities = core.NewCapabilities(append(transcoderCaps, aiCaps...), nil) + n.Capabilities.SetPerCapabilityConstraints(capabilityConstraints) if cfg.OrchMinLivepeerVersion != nil { n.Capabilities.SetMinVersionConstraint(*cfg.OrchMinLivepeerVersion) } diff --git a/core/capabilities.go b/core/capabilities.go index 89a3847bc..be687e94b 100644 --- a/core/capabilities.go +++ b/core/capabilities.go @@ -21,21 +21,21 @@ type ModelConstraint struct { type Capability int type CapabilityString []uint64 type Constraints struct { - minVersion string + minVersion string + perCapability PerCapabilityConstraints } -type PerCapabilityConstraints struct { +type CapabilityConstraints struct { // Models contains a *ModelConstraint for each supported model ID Models ModelConstraints } -type CapabilityConstraints map[Capability]*PerCapabilityConstraints +type PerCapabilityConstraints map[Capability]*CapabilityConstraints type Capabilities struct { - bitstring CapabilityString - mandatories CapabilityString - version string - constraints Constraints - capabilityConstraints CapabilityConstraints - capacities map[Capability]int - mutex sync.Mutex + bitstring CapabilityString + mandatories CapabilityString + version string + constraints Constraints + capacities map[Capability]int + mutex sync.Mutex } type CapabilityTest struct { inVideoData []byte @@ -239,7 +239,7 @@ func (c1 CapabilityString) CompatibleWith(c2 CapabilityString) bool { return true } -func (c1 CapabilityConstraints) CompatibleWith(c2 CapabilityConstraints) bool { +func (c1 PerCapabilityConstraints) CompatibleWith(c2 PerCapabilityConstraints) bool { for c1Cap, c1Constraints := range c1 { c2Constraints, ok := c2[c1Cap] if !ok { @@ -255,7 +255,7 @@ func (c1 CapabilityConstraints) CompatibleWith(c2 CapabilityConstraints) bool { return true } -func (c1 *PerCapabilityConstraints) CompatibleWith(c2 *PerCapabilityConstraints) bool { +func (c1 *CapabilityConstraints) CompatibleWith(c2 *CapabilityConstraints) bool { return c1.Models.CompatibleWith(c2.Models) } @@ -453,8 +453,8 @@ func (bcast *Capabilities) CompatibleWith(orch *net.Capabilities) bool { return false } - orchCapabilityConstraints := CapabilitiesFromNetCapabilities(orch).capabilityConstraints - if !bcast.capabilityConstraints.CompatibleWith(orchCapabilityConstraints) { + orchCapabilityConstraints := CapabilitiesFromNetCapabilities(orch).constraints.perCapability + if !bcast.constraints.perCapability.CompatibleWith(orchCapabilityConstraints) { return false } @@ -467,11 +467,11 @@ func (c *Capabilities) ToNetCapabilities() *net.Capabilities { } c.mutex.Lock() defer c.mutex.Unlock() - netCaps := &net.Capabilities{Bitstring: c.bitstring, Mandatories: c.mandatories, Version: c.version, Capacities: make(map[uint32]uint32), Constraints: &net.Capabilities_Constraints{MinVersion: c.constraints.minVersion}, CapabilityConstraints: make(map[uint32]*net.Capabilities_CapabilityConstraints)} + netCaps := &net.Capabilities{Bitstring: c.bitstring, Mandatories: c.mandatories, Version: c.version, Capacities: make(map[uint32]uint32), Constraints: &net.Capabilities_Constraints{MinVersion: c.constraints.minVersion, PerCapability: make(map[uint32]*net.Capabilities_CapabilityConstraints)}} for capability, capacity := range c.capacities { netCaps.Capacities[uint32(capability)] = uint32(capacity) } - for capability, constraints := range c.capabilityConstraints { + for capability, constraints := range c.constraints.perCapability { models := make(map[string]*net.Capabilities_CapabilityConstraints_ModelConstraint) for modelID, modelConstraint := range constraints.Models { models[modelID] = &net.Capabilities_CapabilityConstraints_ModelConstraint{ @@ -479,7 +479,7 @@ func (c *Capabilities) ToNetCapabilities() *net.Capabilities { } } - netCaps.CapabilityConstraints[uint32(capability)] = &net.Capabilities_CapabilityConstraints{ + netCaps.Constraints.PerCapability[uint32(capability)] = &net.Capabilities_CapabilityConstraints{ Models: models, } } @@ -491,12 +491,11 @@ func CapabilitiesFromNetCapabilities(caps *net.Capabilities) *Capabilities { return nil } coreCaps := &Capabilities{ - bitstring: caps.Bitstring, - mandatories: caps.Mandatories, - capacities: make(map[Capability]int), - version: caps.Version, - constraints: Constraints{minVersion: caps.Constraints.GetMinVersion()}, - capabilityConstraints: make(CapabilityConstraints), + bitstring: caps.Bitstring, + mandatories: caps.Mandatories, + capacities: make(map[Capability]int), + version: caps.Version, + constraints: Constraints{minVersion: caps.Constraints.GetMinVersion(), perCapability: make(PerCapabilityConstraints)}, } if caps.Capacities == nil || len(caps.Capacities) == 0 { // build capacities map if not present (struct received from previous versions) @@ -514,13 +513,13 @@ func CapabilitiesFromNetCapabilities(caps *net.Capabilities) *Capabilities { } } - for capabilityInt, constraints := range caps.CapabilityConstraints { + for capabilityInt, constraints := range caps.Constraints.PerCapability { models := make(map[string]*ModelConstraint) for modelID, modelConstraint := range constraints.Models { models[modelID] = &ModelConstraint{Warm: modelConstraint.Warm} } - coreCaps.capabilityConstraints[Capability(capabilityInt)] = &PerCapabilityConstraints{ + coreCaps.constraints.perCapability[Capability(capabilityInt)] = &CapabilityConstraints{ Models: models, } } @@ -529,7 +528,7 @@ func CapabilitiesFromNetCapabilities(caps *net.Capabilities) *Capabilities { } func NewCapabilities(caps []Capability, m []Capability) *Capabilities { - c := &Capabilities{capacities: make(map[Capability]int), version: LivepeerVersion, capabilityConstraints: make(CapabilityConstraints)} + c := &Capabilities{capacities: make(map[Capability]int), constraints: Constraints{perCapability: make(PerCapabilityConstraints)}, version: LivepeerVersion} if len(caps) > 0 { c.bitstring = NewCapabilityString(caps) // initialize capacities to 1 by default, mandatory capabilities doesn't have capacities @@ -543,13 +542,6 @@ func NewCapabilities(caps []Capability, m []Capability) *Capabilities { return c } -func NewCapabilitiesWithConstraints(caps []Capability, m []Capability, constraints Constraints, capabilityConstraints CapabilityConstraints) *Capabilities { - c := NewCapabilities(caps, m) - c.constraints = constraints - c.capabilityConstraints = capabilityConstraints - return c -} - func (cap *Capabilities) AddCapacity(newCaps *Capabilities) { cap.mutex.Lock() defer cap.mutex.Unlock() @@ -723,6 +715,19 @@ func (bcast *Capabilities) LegacyOnly() bool { return bcast.bitstring.CompatibleWith(legacyCapabilityString) } +func (bcast *Capabilities) SetPerCapabilityConstraints(constraints PerCapabilityConstraints) { + if bcast != nil { + bcast.constraints.perCapability = constraints + } +} + +func (bcast *Capabilities) PerCapability() PerCapabilityConstraints { + if bcast != nil { + return bcast.constraints.perCapability + } + return nil +} + func (bcast *Capabilities) SetMinVersionConstraint(minVersionConstraint string) { if bcast != nil { bcast.constraints.minVersion = minVersionConstraint diff --git a/core/orchestrator.go b/core/orchestrator.go index 70975ff3a..0ec463905 100644 --- a/core/orchestrator.go +++ b/core/orchestrator.go @@ -347,21 +347,23 @@ func (orch *orchestrator) priceInfo(sender ethcommon.Address, manifestID Manifes } } else { // The base price is the sum of the prices of individual capability + model ID pairs - for cap := range caps.Capacities { - // If the capability does not have constraints (and thus any model constraints) skip it - // because we only price a capability together with a model ID right now - constraints, ok := caps.CapabilityConstraints[cap] - if !ok { - continue - } - for modelID := range constraints.Models { - price := orch.node.GetBasePriceForCap(sender.String(), Capability(cap), modelID) - if price == nil { - price = orch.node.GetBasePriceForCap("default", Capability(cap), modelID) + if caps.Constraints != nil && caps.Constraints.PerCapability != nil { + for cap := range caps.Capacities { + // If the capability does not have constraints (and thus any model constraints) skip it + // because we only price a capability together with a model ID right now + constraints, ok := caps.Constraints.PerCapability[cap] + if !ok { + continue } - - if price != nil { - basePrice.Add(basePrice, price) + for modelID := range constraints.Models { + price := orch.node.GetBasePriceForCap(sender.String(), Capability(cap), modelID) + if price == nil { + price = orch.node.GetBasePriceForCap("default", Capability(cap), modelID) + } + + if price != nil { + basePrice.Add(basePrice, price) + } } } } diff --git a/net/lp_rpc.pb.go b/net/lp_rpc.pb.go index 461cbfb79..1b9d2523f 100644 --- a/net/lp_rpc.pb.go +++ b/net/lp_rpc.pb.go @@ -531,13 +531,12 @@ type Capabilities struct { // Bit string of features that are required to be supported Mandatories []uint64 `protobuf:"varint,2,rep,packed,name=mandatories,proto3" json:"mandatories,omitempty"` // Capacity corresponding to each capability - Capacities map[uint32]uint32 `protobuf:"bytes,3,rep,name=capacities,proto3" json:"capacities,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` - Version string `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` - Constraints *Capabilities_Constraints `protobuf:"bytes,5,opt,name=constraints,proto3" json:"constraints,omitempty"` - CapabilityConstraints map[uint32]*Capabilities_CapabilityConstraints `protobuf:"bytes,6,rep,name=capabilityConstraints,proto3" json:"capabilityConstraints,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Capacities map[uint32]uint32 `protobuf:"bytes,3,rep,name=capacities,proto3" json:"capacities,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` + Version string `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` + Constraints *Capabilities_Constraints `protobuf:"bytes,5,opt,name=constraints,proto3" json:"constraints,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Capabilities) Reset() { *m = Capabilities{} } @@ -600,19 +599,13 @@ func (m *Capabilities) GetConstraints() *Capabilities_Constraints { return nil } -func (m *Capabilities) GetCapabilityConstraints() map[uint32]*Capabilities_CapabilityConstraints { - if m != nil { - return m.CapabilityConstraints - } - return nil -} - -// Non-binary general constraints. +// Non-binary constraints. type Capabilities_Constraints struct { - MinVersion string `protobuf:"bytes,1,opt,name=minVersion,proto3" json:"minVersion,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + MinVersion string `protobuf:"bytes,1,opt,name=minVersion,proto3" json:"minVersion,omitempty"` + PerCapability map[uint32]*Capabilities_CapabilityConstraints `protobuf:"bytes,2,rep,name=PerCapability,proto3" json:"PerCapability,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Capabilities_Constraints) Reset() { *m = Capabilities_Constraints{} } @@ -647,6 +640,13 @@ func (m *Capabilities_Constraints) GetMinVersion() string { return "" } +func (m *Capabilities_Constraints) GetPerCapability() map[uint32]*Capabilities_CapabilityConstraints { + if m != nil { + return m.PerCapability + } + return nil +} + // Non-binary capability constraints, such as supported ranges. type Capabilities_CapabilityConstraints struct { Models map[string]*Capabilities_CapabilityConstraints_ModelConstraint `protobuf:"bytes,1,rep,name=models,proto3" json:"models,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` @@ -1882,9 +1882,9 @@ func init() { proto.RegisterType((*S3OSInfo)(nil), "net.S3OSInfo") proto.RegisterType((*PriceInfo)(nil), "net.PriceInfo") proto.RegisterType((*Capabilities)(nil), "net.Capabilities") - proto.RegisterMapType((map[uint32]*Capabilities_CapabilityConstraints)(nil), "net.Capabilities.CapabilityConstraintsEntry") proto.RegisterMapType((map[uint32]uint32)(nil), "net.Capabilities.CapacitiesEntry") proto.RegisterType((*Capabilities_Constraints)(nil), "net.Capabilities.Constraints") + proto.RegisterMapType((map[uint32]*Capabilities_CapabilityConstraints)(nil), "net.Capabilities.Constraints.PerCapabilityEntry") proto.RegisterType((*Capabilities_CapabilityConstraints)(nil), "net.Capabilities.CapabilityConstraints") proto.RegisterMapType((map[string]*Capabilities_CapabilityConstraints_ModelConstraint)(nil), "net.Capabilities.CapabilityConstraints.ModelsEntry") proto.RegisterType((*Capabilities_CapabilityConstraints_ModelConstraint)(nil), "net.Capabilities.CapabilityConstraints.ModelConstraint") @@ -1909,132 +1909,132 @@ func init() { } var fileDescriptor_034e29c79f9ba827 = []byte{ - // 2021 bytes of a gzipped FileDescriptorProto + // 2022 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x58, 0xdd, 0x72, 0xdb, 0xc6, - 0xf5, 0x17, 0x3f, 0xc4, 0x8f, 0x43, 0x52, 0x82, 0xd6, 0x96, 0x0c, 0x33, 0x76, 0xfe, 0x32, 0x12, - 0xe5, 0xef, 0xcc, 0xd4, 0x8a, 0x87, 0x92, 0x9d, 0xb8, 0x33, 0x99, 0x56, 0x1f, 0xb4, 0xc4, 0xd4, - 0x92, 0x38, 0x4b, 0xd9, 0x33, 0xed, 0x45, 0x59, 0x08, 0x58, 0x92, 0xa8, 0x48, 0x00, 0x5a, 0x2c, - 0x63, 0x29, 0xd3, 0x17, 0xe8, 0x23, 0xb4, 0x37, 0x9d, 0xe9, 0x4c, 0x9f, 0xa0, 0x2f, 0xd0, 0x07, - 0xe8, 0x03, 0xf4, 0x41, 0x7a, 0xdf, 0xce, 0x9e, 0x5d, 0x80, 0x80, 0x48, 0x27, 0xaa, 0xef, 0xf6, - 0x7c, 0xee, 0xd9, 0xb3, 0x67, 0x7f, 0xe7, 0x00, 0x60, 0xf8, 0x4c, 0x7c, 0x35, 0x0e, 0xfb, 0x3c, - 0x74, 0xb6, 0x43, 0x1e, 0x88, 0x80, 0x14, 0x7c, 0x26, 0xac, 0x4d, 0xa8, 0x74, 0x3d, 0x7f, 0xd8, - 0x0d, 0xfc, 0x21, 0xb9, 0x0f, 0xcb, 0xdf, 0xdb, 0xe3, 0x29, 0x33, 0x73, 0x9b, 0xb9, 0xa7, 0x75, - 0xaa, 0x08, 0xeb, 0x04, 0x1e, 0xb5, 0x7d, 0xf7, 0x9c, 0xdb, 0x7e, 0xe4, 0x04, 0xae, 0xe7, 0x0f, - 0x7b, 0x2c, 0x8a, 0xbc, 0xc0, 0xa7, 0xec, 0x6a, 0xca, 0x22, 0x41, 0x9e, 0x01, 0xd8, 0x53, 0x31, - 0xea, 0x8b, 0xe0, 0x92, 0xf9, 0x68, 0x5a, 0x6b, 0xad, 0x6c, 0xfb, 0x4c, 0x6c, 0xef, 0x4d, 0xc5, - 0xe8, 0x5c, 0x72, 0x69, 0xd5, 0x8e, 0x97, 0xd6, 0xff, 0xc1, 0xe3, 0x0f, 0xb8, 0x8b, 0xc2, 0xc0, - 0x8f, 0x98, 0x75, 0x0d, 0xf7, 0xce, 0xb8, 0x33, 0x62, 0x91, 0xe0, 0xb6, 0x08, 0x78, 0xbc, 0x8d, - 0x09, 0x65, 0xdb, 0x75, 0x39, 0x8b, 0x22, 0x1d, 0x5e, 0x4c, 0x12, 0x03, 0x0a, 0x91, 0x37, 0x34, - 0xf3, 0xc8, 0x95, 0x4b, 0xf2, 0x02, 0xea, 0x8e, 0x1d, 0xda, 0x17, 0xde, 0xd8, 0x13, 0x1e, 0x8b, - 0xcc, 0x02, 0x06, 0xb5, 0x86, 0x41, 0x1d, 0xa4, 0x04, 0x34, 0xa3, 0x66, 0xfd, 0x29, 0x07, 0xa5, - 0xb3, 0x5e, 0xc7, 0x1f, 0x04, 0xe4, 0x15, 0xd4, 0x22, 0x11, 0x70, 0x7b, 0xc8, 0xce, 0x6f, 0x42, - 0x95, 0x90, 0x95, 0xd6, 0x03, 0x74, 0xa0, 0x34, 0xb6, 0x7b, 0x33, 0x31, 0x4d, 0xeb, 0x92, 0x2d, - 0x28, 0x45, 0x3b, 0x9e, 0x3f, 0x08, 0x4c, 0x03, 0xb7, 0x6d, 0xa0, 0x55, 0x6f, 0x47, 0xd9, 0x51, - 0x2d, 0xb4, 0x9e, 0x41, 0x2d, 0xe5, 0x82, 0x00, 0x94, 0x0e, 0x3b, 0xb4, 0x7d, 0x70, 0x6e, 0x2c, - 0x91, 0x12, 0xe4, 0x7b, 0x3b, 0x46, 0x4e, 0xf2, 0x8e, 0xce, 0xce, 0x8e, 0xde, 0xb4, 0x8d, 0xbc, - 0xf5, 0xd7, 0x1c, 0x54, 0x62, 0x1f, 0x84, 0x40, 0x71, 0x14, 0x44, 0x02, 0xc3, 0xaa, 0x52, 0x5c, - 0xcb, 0x2c, 0x5c, 0xb2, 0x1b, 0xcc, 0x42, 0x95, 0xca, 0x25, 0xd9, 0x80, 0x52, 0x18, 0x8c, 0x3d, - 0xe7, 0x06, 0xcf, 0x5f, 0xa5, 0x9a, 0x22, 0x8f, 0xa0, 0x1a, 0x79, 0x43, 0xdf, 0x16, 0x53, 0xce, - 0xcc, 0x22, 0x8a, 0x66, 0x0c, 0xf2, 0x29, 0x80, 0xc3, 0x99, 0xcb, 0x7c, 0xe1, 0xd9, 0x63, 0x73, - 0x19, 0xc5, 0x29, 0x0e, 0x69, 0x42, 0xe5, 0x7a, 0x6f, 0xf2, 0xc3, 0xa1, 0x2d, 0x98, 0x59, 0x42, - 0x69, 0x42, 0x5b, 0x6f, 0xa1, 0xda, 0xe5, 0x9e, 0xc3, 0x30, 0x48, 0x0b, 0xea, 0xa1, 0x24, 0xba, - 0x8c, 0xbf, 0xf5, 0x3d, 0x15, 0x6c, 0x81, 0x66, 0x78, 0xe4, 0x73, 0x68, 0x84, 0xde, 0x35, 0x1b, - 0x47, 0xb1, 0x52, 0x1e, 0x95, 0xb2, 0x4c, 0xeb, 0xef, 0x25, 0xa8, 0xa7, 0xaf, 0x4d, 0x9e, 0xe0, - 0xc2, 0x13, 0x91, 0xe0, 0x9e, 0x3f, 0x34, 0x73, 0x9b, 0x85, 0xa7, 0x45, 0x3a, 0x63, 0x90, 0x4d, - 0xa8, 0x4d, 0x6c, 0xdf, 0x95, 0xc5, 0x23, 0x2f, 0x3f, 0x8f, 0xf2, 0x34, 0x8b, 0xec, 0x01, 0xc8, - 0x8b, 0x77, 0xe2, 0xea, 0x28, 0x3c, 0xad, 0xb5, 0x9e, 0xcc, 0x55, 0x07, 0x12, 0x4a, 0xa7, 0xed, - 0x0b, 0x7e, 0x43, 0x53, 0x46, 0xb2, 0x1c, 0xbf, 0x67, 0x5c, 0x16, 0xae, 0x4e, 0x61, 0x4c, 0x92, - 0x5f, 0x40, 0xcd, 0x09, 0x7c, 0x59, 0xbd, 0x9e, 0x2f, 0x22, 0xcc, 0x60, 0xad, 0xf5, 0x78, 0x81, - 0xf7, 0x99, 0x12, 0x4d, 0x5b, 0x90, 0x0b, 0x58, 0x4f, 0xca, 0xf2, 0x26, 0xa5, 0x65, 0x96, 0x30, - 0xd0, 0x9f, 0x2d, 0x0e, 0x74, 0x4e, 0x5d, 0xc5, 0xbc, 0xd8, 0x55, 0xf3, 0x5b, 0x58, 0xbd, 0x75, - 0xba, 0xb8, 0x80, 0xe4, 0x35, 0x35, 0x54, 0x01, 0x25, 0x78, 0x90, 0x47, 0x9e, 0x22, 0x7e, 0x9e, - 0xff, 0x26, 0xd7, 0x7c, 0x06, 0xb5, 0x94, 0x37, 0x59, 0x33, 0x13, 0xcf, 0x7f, 0xa7, 0xf3, 0xa1, - 0xaa, 0x32, 0xc5, 0x69, 0xfe, 0x27, 0x07, 0xeb, 0x0b, 0x63, 0x24, 0xbf, 0x82, 0xd2, 0x24, 0x70, - 0xd9, 0x38, 0xc2, 0x6b, 0xac, 0xb5, 0x76, 0xee, 0x78, 0xb8, 0xed, 0x13, 0xb4, 0x52, 0x67, 0xd4, - 0x2e, 0x9a, 0x5b, 0xb0, 0x8a, 0xec, 0x99, 0x9e, 0x7c, 0x29, 0xef, 0x6d, 0x3e, 0xc1, 0x98, 0x2a, - 0x14, 0xd7, 0x4d, 0x0e, 0xb5, 0x94, 0x75, 0xfa, 0xdc, 0xfa, 0xe1, 0x9c, 0xa4, 0xcf, 0x5d, 0x6b, - 0x7d, 0xfd, 0x3f, 0xc5, 0x34, 0x63, 0xa4, 0x13, 0x76, 0x05, 0xcd, 0x0f, 0x5f, 0xd2, 0x82, 0xd4, - 0x7f, 0x9b, 0x0d, 0xe1, 0xff, 0xef, 0x18, 0x42, 0x6a, 0x4b, 0xeb, 0x1f, 0x79, 0x30, 0xd2, 0x40, - 0x8a, 0x8f, 0xf2, 0x53, 0x00, 0xa1, 0xa1, 0x97, 0xf1, 0xf8, 0xa6, 0x66, 0x1c, 0xf2, 0x12, 0x1a, - 0xc2, 0x73, 0x2e, 0x99, 0xe8, 0x87, 0x36, 0xb7, 0x27, 0x91, 0xde, 0x5f, 0x41, 0xe7, 0x39, 0x4a, - 0xba, 0x28, 0xa0, 0x75, 0x91, 0xa2, 0x64, 0x13, 0xc0, 0x87, 0xdd, 0x47, 0xe0, 0x2b, 0xa4, 0x9a, - 0x40, 0x02, 0x08, 0xb4, 0x1a, 0x26, 0xd8, 0x90, 0x02, 0xf3, 0x62, 0x16, 0xcc, 0x6f, 0x43, 0xf7, - 0xf2, 0x9d, 0xa0, 0xfb, 0x56, 0x13, 0x2a, 0xfd, 0x44, 0x13, 0x22, 0x5b, 0x50, 0xd6, 0x90, 0x6d, - 0x6e, 0x62, 0xdd, 0xd5, 0x52, 0xd0, 0x4e, 0x63, 0x99, 0xf5, 0x3b, 0xa8, 0x26, 0xe6, 0xf2, 0x35, - 0xcc, 0x5a, 0x5c, 0x9d, 0x2a, 0x82, 0x3c, 0x06, 0x88, 0x54, 0x03, 0xeb, 0x7b, 0xae, 0x46, 0xdf, - 0xaa, 0xe6, 0x74, 0x5c, 0x99, 0x6f, 0x76, 0x1d, 0x7a, 0xdc, 0x16, 0xf2, 0x65, 0x14, 0x10, 0xdd, - 0x52, 0x1c, 0xeb, 0xdf, 0x45, 0x28, 0xf7, 0xd8, 0xf0, 0xd0, 0x16, 0x36, 0xbe, 0x22, 0xdb, 0xf7, - 0x06, 0x2c, 0x12, 0x1d, 0x57, 0xef, 0x92, 0xe2, 0x60, 0x9f, 0x63, 0x57, 0x1a, 0x22, 0xe5, 0x12, - 0xfb, 0x80, 0x1d, 0x8d, 0xd0, 0x6f, 0x9d, 0xe2, 0x5a, 0xe2, 0x73, 0xc8, 0x83, 0x81, 0x37, 0x66, - 0x71, 0x6e, 0x13, 0x3a, 0xee, 0x94, 0xcb, 0xb3, 0x4e, 0xd9, 0x84, 0x8a, 0x3b, 0xd5, 0xd1, 0xc9, - 0xac, 0x2d, 0xd3, 0x84, 0x9e, 0xbb, 0x8a, 0xf2, 0xc7, 0x5c, 0x45, 0xe5, 0xa7, 0xae, 0xe2, 0x39, - 0xdc, 0x77, 0xec, 0xb1, 0xd3, 0x0f, 0x19, 0x77, 0x58, 0x28, 0xa6, 0xf6, 0xb8, 0x8f, 0x67, 0x02, - 0x7c, 0xb1, 0x44, 0xca, 0xba, 0x89, 0xe8, 0x58, 0x9e, 0xf0, 0x6e, 0x97, 0x27, 0xc3, 0x1f, 0x4c, - 0xc7, 0xe3, 0x6e, 0x9c, 0x8c, 0x27, 0xa8, 0xab, 0xc2, 0x7f, 0xe7, 0xb9, 0x2c, 0xd0, 0x12, 0x9a, - 0x51, 0x23, 0x5f, 0x43, 0x23, 0x4d, 0xb7, 0x4c, 0xeb, 0x43, 0x76, 0x59, 0xbd, 0xdb, 0x86, 0x3b, - 0xe6, 0x67, 0x77, 0x32, 0xdc, 0x21, 0x7b, 0x40, 0x22, 0x36, 0x9c, 0x30, 0x5f, 0x3f, 0x3a, 0x26, - 0x18, 0x8f, 0xcc, 0x2d, 0x4c, 0x1c, 0x51, 0xc3, 0x03, 0x1b, 0x76, 0x13, 0x09, 0x5d, 0xd3, 0xda, - 0x33, 0x16, 0xd9, 0x06, 0xf2, 0x3a, 0xe0, 0x0e, 0x4b, 0x66, 0x29, 0x4f, 0x36, 0xd3, 0x2f, 0x54, - 0x0a, 0xe7, 0x25, 0xd6, 0x0e, 0x34, 0x32, 0x3e, 0x65, 0x25, 0x0d, 0x78, 0xa0, 0x70, 0xb2, 0x48, - 0x71, 0x4d, 0x56, 0x20, 0x2f, 0x02, 0x2c, 0xb7, 0x22, 0xcd, 0x8b, 0xc0, 0xfa, 0xe7, 0x32, 0xd4, - 0xd3, 0xe7, 0x90, 0x46, 0xbe, 0x3d, 0x61, 0x38, 0xe7, 0x54, 0x29, 0xae, 0xe5, 0x2b, 0x79, 0xef, - 0xb9, 0x62, 0x64, 0xae, 0x61, 0x35, 0x29, 0x42, 0x8e, 0x22, 0x23, 0xe6, 0x0d, 0x47, 0xc2, 0x24, - 0xc8, 0xd6, 0x94, 0xc4, 0x81, 0x0b, 0x4f, 0xc2, 0x13, 0x33, 0xef, 0xa1, 0x20, 0x26, 0x65, 0xa9, - 0x0e, 0xc2, 0xc8, 0xbc, 0xaf, 0x20, 0x71, 0x10, 0x46, 0xe4, 0x39, 0x94, 0x06, 0x01, 0x9f, 0xd8, - 0xc2, 0x5c, 0xc7, 0x69, 0xcc, 0x9c, 0x4b, 0xec, 0xf6, 0x6b, 0x94, 0x53, 0xad, 0x27, 0x77, 0x1d, - 0x84, 0xd1, 0x21, 0xf3, 0xcd, 0x0d, 0x74, 0xa3, 0x29, 0xb2, 0x03, 0x65, 0xfd, 0x24, 0xcc, 0x07, - 0xe8, 0xea, 0xe1, 0xbc, 0xab, 0xf8, 0xae, 0x62, 0x4d, 0x19, 0xd0, 0x30, 0x08, 0x4d, 0x13, 0xc3, - 0x94, 0x4b, 0xf2, 0x12, 0xca, 0xcc, 0x57, 0x40, 0xfa, 0x10, 0xdd, 0x3c, 0x9a, 0x77, 0x83, 0xc4, - 0x41, 0xe0, 0x32, 0x87, 0xc6, 0xca, 0x38, 0x61, 0x05, 0xe3, 0x80, 0x1f, 0xb2, 0x50, 0x8c, 0xcc, - 0x26, 0x3a, 0x4c, 0x71, 0xc8, 0x11, 0xd4, 0x9d, 0x11, 0x0f, 0x26, 0xb6, 0x3a, 0x8e, 0xf9, 0x09, - 0x3a, 0xff, 0x6c, 0xde, 0xf9, 0x01, 0x6a, 0xf5, 0xa6, 0x17, 0x91, 0x3d, 0x09, 0xc7, 0x9e, 0x3f, - 0xa4, 0x19, 0x43, 0x99, 0xdd, 0xab, 0xa9, 0x2d, 0x5b, 0x84, 0xf9, 0x08, 0x13, 0x10, 0x93, 0xd6, - 0x63, 0x28, 0x69, 0x1d, 0x80, 0xd2, 0x49, 0xb7, 0x7d, 0x74, 0xde, 0x33, 0x96, 0x48, 0x19, 0x0a, - 0x27, 0xdd, 0x5d, 0x23, 0x67, 0xfd, 0x1e, 0xca, 0xf1, 0x1d, 0xdf, 0x83, 0xd5, 0xf6, 0xe9, 0xc1, - 0xd9, 0x61, 0x9b, 0xf6, 0x0f, 0xdb, 0xaf, 0xf7, 0xde, 0xbe, 0x91, 0x03, 0xea, 0x1a, 0x34, 0x8e, - 0x5b, 0x2f, 0x77, 0xfb, 0xfb, 0x7b, 0xbd, 0xf6, 0x9b, 0xce, 0x69, 0xdb, 0xc8, 0x91, 0x06, 0x54, - 0x91, 0x75, 0xb2, 0xd7, 0x39, 0x35, 0xf2, 0x09, 0x79, 0xdc, 0x39, 0x3a, 0x36, 0x0a, 0xe4, 0x21, - 0xac, 0x23, 0x79, 0x70, 0x76, 0xda, 0x3b, 0xa7, 0x7b, 0x9d, 0xd3, 0xf6, 0xa1, 0x12, 0x15, 0xad, - 0x16, 0xc0, 0x2c, 0x49, 0xa4, 0x02, 0x45, 0xa9, 0x68, 0x2c, 0xe9, 0xd5, 0x0b, 0x23, 0x27, 0xc3, - 0x7a, 0xd7, 0xfd, 0xc6, 0xc8, 0xab, 0xc5, 0x2b, 0xa3, 0x60, 0x1d, 0xc0, 0xda, 0xdc, 0xd9, 0xc9, - 0x0a, 0xc0, 0xc1, 0x31, 0x3d, 0x3b, 0xd9, 0xeb, 0xef, 0xb6, 0x9e, 0x1b, 0x4b, 0x19, 0xba, 0x65, - 0xe4, 0xd2, 0xf4, 0xee, 0xae, 0x91, 0xb7, 0xae, 0x60, 0x3d, 0xfe, 0x0a, 0x61, 0x6e, 0x4f, 0x3d, - 0x29, 0xc4, 0x61, 0x03, 0x0a, 0x53, 0x3e, 0x8e, 0x07, 0x82, 0x29, 0x1f, 0xe3, 0x24, 0x8d, 0x13, - 0xa9, 0x06, 0x5f, 0x4d, 0x91, 0x6d, 0xb8, 0x77, 0x0b, 0xb6, 0xfa, 0xd2, 0x52, 0x8d, 0xdb, 0x6b, - 0x61, 0x06, 0xb6, 0xde, 0xf2, 0xb1, 0xf5, 0x6b, 0x68, 0x24, 0x5b, 0xe2, 0x56, 0x2f, 0xa1, 0xa2, - 0x1f, 0x73, 0x3c, 0x00, 0x35, 0x55, 0xa7, 0x5d, 0x14, 0x18, 0x4d, 0x74, 0xe7, 0x3f, 0x79, 0xac, - 0x3f, 0xe7, 0x60, 0x35, 0xb1, 0xa2, 0x2c, 0x9a, 0x8e, 0x45, 0xdc, 0x30, 0x72, 0xb3, 0x86, 0xb1, - 0x01, 0xcb, 0x8c, 0xf3, 0x80, 0xab, 0x46, 0x75, 0xbc, 0x44, 0x15, 0x49, 0x9e, 0x42, 0xd1, 0xb5, - 0x85, 0xad, 0x1b, 0x37, 0xc9, 0xc6, 0x20, 0xf7, 0x3e, 0x5e, 0xa2, 0xa8, 0x41, 0xbe, 0x84, 0x62, - 0xea, 0xdb, 0x66, 0x5d, 0x21, 0xef, 0xad, 0x29, 0x83, 0xa2, 0xca, 0x7e, 0x05, 0x4a, 0x1c, 0x03, - 0xb1, 0xfe, 0x00, 0xab, 0x94, 0x0d, 0xbd, 0x48, 0xb0, 0xe4, 0x73, 0x6e, 0x03, 0x4a, 0x11, 0x73, - 0x38, 0x8b, 0x3f, 0x62, 0x34, 0x25, 0x1b, 0x92, 0x9e, 0xb2, 0x6f, 0x74, 0xb2, 0x13, 0xfa, 0x63, - 0x3f, 0xeb, 0xfe, 0x98, 0x83, 0xc6, 0x69, 0x20, 0xbc, 0xc1, 0x8d, 0x4e, 0xe6, 0x82, 0x1b, 0xfe, - 0x02, 0xca, 0x91, 0x6a, 0xc3, 0xda, 0x6b, 0x3d, 0x06, 0x5e, 0xcc, 0x7c, 0x2c, 0x94, 0x61, 0x0b, - 0x3b, 0xba, 0xec, 0xb8, 0x98, 0x80, 0x02, 0xd5, 0x54, 0xa6, 0xeb, 0xae, 0x65, 0xbb, 0xee, 0x77, - 0xc5, 0x4a, 0xde, 0x28, 0x7c, 0x57, 0xac, 0x3c, 0x31, 0x2c, 0xeb, 0x2f, 0x79, 0xa8, 0xa7, 0xc7, - 0x28, 0xf9, 0x29, 0xc3, 0x99, 0xe3, 0x85, 0x1e, 0xf3, 0x85, 0xee, 0xf9, 0x33, 0x86, 0x9c, 0x2e, - 0x06, 0xb6, 0xc3, 0xfa, 0xb3, 0x59, 0xb0, 0x4e, 0xab, 0x92, 0xf3, 0x4e, 0x32, 0xc8, 0x43, 0xa8, - 0xbc, 0xf7, 0xfc, 0x7e, 0xc8, 0x83, 0x0b, 0x3d, 0x03, 0x94, 0xdf, 0x7b, 0x7e, 0x97, 0x07, 0x17, - 0xb2, 0x34, 0x13, 0x37, 0x7d, 0x6e, 0xfb, 0xae, 0xea, 0xaa, 0x6a, 0x22, 0x58, 0x4b, 0x44, 0xd4, - 0xf6, 0x5d, 0x6c, 0xaa, 0x04, 0x8a, 0x11, 0x63, 0xae, 0x9e, 0x0d, 0x70, 0x4d, 0xbe, 0x04, 0x63, - 0x36, 0xaa, 0xf4, 0x2f, 0xc6, 0x81, 0x73, 0x89, 0x43, 0x42, 0x9d, 0xae, 0xce, 0xf8, 0xfb, 0x92, - 0x4d, 0x8e, 0x61, 0x2d, 0xa5, 0xaa, 0x67, 0x47, 0x35, 0x30, 0x7c, 0x92, 0x9a, 0x1d, 0xdb, 0x89, - 0x8e, 0x9e, 0x22, 0x53, 0x1b, 0x28, 0x8e, 0xd5, 0x01, 0xa2, 0x74, 0x7b, 0xcc, 0x77, 0x19, 0xd7, - 0x69, 0x7a, 0x02, 0xf5, 0x08, 0xe9, 0xbe, 0x1f, 0xf8, 0x0e, 0xd3, 0xa3, 0x72, 0x4d, 0xf1, 0x4e, - 0x25, 0x6b, 0xc1, 0x9b, 0xf8, 0x01, 0x36, 0x16, 0x6f, 0x4b, 0xb6, 0x60, 0xc5, 0xe1, 0x4c, 0x05, - 0xcb, 0x83, 0xa9, 0xef, 0xea, 0x47, 0xd2, 0x88, 0xb9, 0x54, 0x32, 0xc9, 0x2b, 0x78, 0x98, 0x55, - 0x53, 0x49, 0x50, 0xa9, 0x54, 0x1b, 0x6d, 0x64, 0x2c, 0x30, 0x19, 0x32, 0x9f, 0xd6, 0xdf, 0xf2, - 0x50, 0xee, 0xda, 0x37, 0x58, 0x6e, 0x73, 0x43, 0x75, 0xee, 0x6e, 0x43, 0x35, 0xbe, 0x11, 0x79, - 0x40, 0xbd, 0x97, 0xa6, 0x16, 0x27, 0xbb, 0xf0, 0x11, 0xc9, 0x26, 0x1d, 0xb8, 0xaf, 0x23, 0xd3, - 0xd9, 0xd5, 0xce, 0x8a, 0x88, 0x45, 0x0f, 0x52, 0xce, 0xd2, 0xb7, 0x41, 0x89, 0x98, 0xbf, 0xa1, - 0x17, 0xb0, 0xc2, 0xae, 0x43, 0xe6, 0x08, 0xe6, 0xf6, 0x71, 0xd0, 0xd7, 0xa3, 0xfb, 0xed, 0xaf, - 0x80, 0x46, 0xac, 0x85, 0xac, 0xd6, 0xbf, 0x72, 0x50, 0x4f, 0xe3, 0x07, 0xd9, 0x87, 0xd5, 0x23, - 0x26, 0x32, 0x2c, 0x73, 0x0e, 0x65, 0x34, 0x8a, 0x34, 0x17, 0xe3, 0x0f, 0xf9, 0x2d, 0xac, 0x2f, - 0xfc, 0xc7, 0x44, 0xd4, 0x47, 0xfe, 0x8f, 0xfd, 0xce, 0x6a, 0x5a, 0x3f, 0xa6, 0xa2, 0x7e, 0x51, - 0x91, 0xcf, 0xa1, 0xd8, 0x95, 0x2d, 0x47, 0xfd, 0xda, 0x89, 0xff, 0x9f, 0x35, 0xb3, 0x64, 0xeb, - 0x14, 0xe0, 0x7c, 0xf6, 0x65, 0xf5, 0x4b, 0x20, 0x31, 0x06, 0xa6, 0xb8, 0xf7, 0xd1, 0xe4, 0x16, - 0x38, 0x36, 0x15, 0x00, 0x67, 0x30, 0xeb, 0x79, 0x6e, 0xbf, 0xfc, 0x9b, 0xe5, 0xed, 0xaf, 0x7c, - 0x26, 0x2e, 0x4a, 0xf8, 0xff, 0x6e, 0xe7, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xa2, 0xc3, 0xc1, - 0x2e, 0xd3, 0x13, 0x00, 0x00, + 0x15, 0x16, 0x7f, 0xc4, 0x9f, 0x43, 0x52, 0x82, 0xd6, 0x96, 0x0c, 0x33, 0x76, 0x6a, 0x23, 0x71, + 0xea, 0x5c, 0x84, 0xf1, 0x50, 0xb2, 0x13, 0x77, 0x26, 0xd3, 0xea, 0x87, 0x96, 0x98, 0x5a, 0x12, + 0x67, 0x29, 0x6b, 0xa6, 0xbd, 0x28, 0x0b, 0x01, 0x4b, 0x12, 0x15, 0x09, 0xc0, 0x8b, 0x65, 0x2c, + 0x65, 0xfa, 0x02, 0x7d, 0x84, 0xf6, 0xa6, 0x9d, 0xce, 0xf4, 0x3d, 0xfa, 0x00, 0x7d, 0x80, 0x3e, + 0x46, 0x2f, 0x7a, 0xdf, 0xce, 0x9e, 0x5d, 0x80, 0x80, 0xc8, 0x38, 0x8a, 0xef, 0xf6, 0xfc, 0xee, + 0xd9, 0xb3, 0x7b, 0xbe, 0x73, 0x00, 0x30, 0x7c, 0x26, 0xbe, 0x9c, 0x84, 0x03, 0x1e, 0x3a, 0xad, + 0x90, 0x07, 0x22, 0x20, 0x05, 0x9f, 0x09, 0xeb, 0x11, 0x54, 0x7a, 0x9e, 0x3f, 0xea, 0x05, 0xfe, + 0x88, 0xdc, 0x85, 0xd5, 0xef, 0xec, 0xc9, 0x8c, 0x99, 0xb9, 0x47, 0xb9, 0xa7, 0x75, 0xaa, 0x08, + 0xeb, 0x18, 0x1e, 0x74, 0x7c, 0xf7, 0x8c, 0xdb, 0x7e, 0xe4, 0x04, 0xae, 0xe7, 0x8f, 0xfa, 0x2c, + 0x8a, 0xbc, 0xc0, 0xa7, 0xec, 0xed, 0x8c, 0x45, 0x82, 0x7c, 0x01, 0x60, 0xcf, 0xc4, 0x78, 0x20, + 0x82, 0x4b, 0xe6, 0xa3, 0x69, 0xad, 0xbd, 0xd6, 0xf2, 0x99, 0x68, 0xed, 0xce, 0xc4, 0xf8, 0x4c, + 0x72, 0x69, 0xd5, 0x8e, 0x97, 0xd6, 0xcf, 0xe0, 0xe1, 0x0f, 0xb8, 0x8b, 0xc2, 0xc0, 0x8f, 0x98, + 0x75, 0x05, 0x77, 0x4e, 0xb9, 0x33, 0x66, 0x91, 0xe0, 0xb6, 0x08, 0x78, 0xbc, 0x8d, 0x09, 0x65, + 0xdb, 0x75, 0x39, 0x8b, 0x22, 0x1d, 0x5e, 0x4c, 0x12, 0x03, 0x0a, 0x91, 0x37, 0x32, 0xf3, 0xc8, + 0x95, 0x4b, 0xf2, 0x1c, 0xea, 0x8e, 0x1d, 0xda, 0x17, 0xde, 0xc4, 0x13, 0x1e, 0x8b, 0xcc, 0x02, + 0x06, 0xb5, 0x81, 0x41, 0xed, 0xa7, 0x04, 0x34, 0xa3, 0x66, 0xfd, 0x39, 0x07, 0xa5, 0xd3, 0x7e, + 0xd7, 0x1f, 0x06, 0xe4, 0x25, 0xd4, 0x22, 0x11, 0x70, 0x7b, 0xc4, 0xce, 0xae, 0x43, 0x95, 0x90, + 0xb5, 0xf6, 0x3d, 0x74, 0xa0, 0x34, 0x5a, 0xfd, 0xb9, 0x98, 0xa6, 0x75, 0xc9, 0x13, 0x28, 0x45, + 0xdb, 0x9e, 0x3f, 0x0c, 0x4c, 0x03, 0xb7, 0x6d, 0xa0, 0x55, 0x7f, 0x5b, 0xd9, 0x51, 0x2d, 0xb4, + 0xbe, 0x80, 0x5a, 0xca, 0x05, 0x01, 0x28, 0x1d, 0x74, 0x69, 0x67, 0xff, 0xcc, 0x58, 0x21, 0x25, + 0xc8, 0xf7, 0xb7, 0x8d, 0x9c, 0xe4, 0x1d, 0x9e, 0x9e, 0x1e, 0xbe, 0xee, 0x18, 0x79, 0xeb, 0xef, + 0x39, 0xa8, 0xc4, 0x3e, 0x08, 0x81, 0xe2, 0x38, 0x88, 0x04, 0x86, 0x55, 0xa5, 0xb8, 0x96, 0x59, + 0xb8, 0x64, 0xd7, 0x98, 0x85, 0x2a, 0x95, 0x4b, 0xb2, 0x05, 0xa5, 0x30, 0x98, 0x78, 0xce, 0x35, + 0x9e, 0xbf, 0x4a, 0x35, 0x45, 0x1e, 0x40, 0x35, 0xf2, 0x46, 0xbe, 0x2d, 0x66, 0x9c, 0x99, 0x45, + 0x14, 0xcd, 0x19, 0xe4, 0x63, 0x00, 0x87, 0x33, 0x97, 0xf9, 0xc2, 0xb3, 0x27, 0xe6, 0x2a, 0x8a, + 0x53, 0x1c, 0xd2, 0x84, 0xca, 0xd5, 0xee, 0xf4, 0xfb, 0x03, 0x5b, 0x30, 0xb3, 0x84, 0xd2, 0x84, + 0xb6, 0xde, 0x40, 0xb5, 0xc7, 0x3d, 0x87, 0x61, 0x90, 0x16, 0xd4, 0x43, 0x49, 0xf4, 0x18, 0x7f, + 0xe3, 0x7b, 0x2a, 0xd8, 0x02, 0xcd, 0xf0, 0xc8, 0xa7, 0xd0, 0x08, 0xbd, 0x2b, 0x36, 0x89, 0x62, + 0xa5, 0x3c, 0x2a, 0x65, 0x99, 0xd6, 0xdf, 0x4a, 0x50, 0x4f, 0x5f, 0x9b, 0x3c, 0xc1, 0x85, 0x27, + 0x22, 0xc1, 0x3d, 0x7f, 0x64, 0xe6, 0x1e, 0x15, 0x9e, 0x16, 0xe9, 0x9c, 0x41, 0x1e, 0x41, 0x6d, + 0x6a, 0xfb, 0xae, 0x7c, 0x3c, 0xf2, 0xf2, 0xf3, 0x28, 0x4f, 0xb3, 0xc8, 0x2e, 0x80, 0xbc, 0x78, + 0x27, 0x7e, 0x1d, 0x85, 0xa7, 0xb5, 0xf6, 0xe3, 0x85, 0xd7, 0x81, 0x84, 0xd2, 0xe9, 0xf8, 0x82, + 0x5f, 0xd3, 0x94, 0x91, 0x7c, 0x8e, 0xdf, 0x31, 0x2e, 0x1f, 0xae, 0x4e, 0x61, 0x4c, 0x92, 0x5f, + 0x42, 0xcd, 0x09, 0x7c, 0xf9, 0x7a, 0x3d, 0x5f, 0x44, 0x98, 0xc1, 0x5a, 0xfb, 0xe1, 0x12, 0xef, + 0x73, 0x25, 0x9a, 0xb6, 0x68, 0x7e, 0x03, 0xeb, 0x37, 0x76, 0x8e, 0x2f, 0x57, 0xa6, 0xb0, 0xa1, + 0x2e, 0x37, 0xa9, 0xd5, 0x3c, 0xf2, 0x14, 0xf1, 0x8b, 0xfc, 0xd7, 0xb9, 0xe6, 0x7f, 0x72, 0x50, + 0x4b, 0xf9, 0x96, 0x17, 0x3a, 0xf5, 0xfc, 0x73, 0x1d, 0xac, 0x7a, 0x32, 0x29, 0x0e, 0x39, 0x87, + 0x46, 0x8f, 0xf1, 0x24, 0xb4, 0x6b, 0x4c, 0x58, 0xad, 0xfd, 0xec, 0xbd, 0x11, 0xb7, 0x32, 0x26, + 0x2a, 0x3d, 0x59, 0x37, 0x4d, 0x0f, 0xc8, 0xa2, 0xd2, 0x92, 0x93, 0x7c, 0x93, 0x3e, 0x49, 0xad, + 0xfd, 0xf3, 0xe5, 0xf7, 0xa0, 0x7c, 0xa4, 0x73, 0x96, 0x3a, 0xf2, 0xff, 0x72, 0xb0, 0xb9, 0x54, + 0x89, 0xfc, 0x1a, 0x4a, 0xd3, 0xc0, 0x65, 0x93, 0x08, 0x9f, 0x49, 0xad, 0xbd, 0x7d, 0x4b, 0xef, + 0xad, 0x63, 0xb4, 0x52, 0x07, 0xd3, 0x2e, 0x9a, 0x4f, 0x60, 0x1d, 0xd9, 0x73, 0x3d, 0x59, 0x89, + 0xef, 0x6c, 0x3e, 0xc5, 0xf3, 0x54, 0x28, 0xae, 0x9b, 0x1c, 0x6a, 0x29, 0xeb, 0xf4, 0x89, 0x75, + 0x61, 0x1e, 0x67, 0x4f, 0xfc, 0xd5, 0x4f, 0x8a, 0x69, 0xce, 0x48, 0x65, 0xc0, 0xfa, 0x67, 0x1e, + 0x8c, 0x34, 0x6a, 0x62, 0x05, 0x7e, 0x0c, 0x20, 0x34, 0xce, 0x32, 0x1e, 0xdf, 0xfc, 0x9c, 0x43, + 0x5e, 0x40, 0x43, 0x78, 0xce, 0x25, 0x13, 0x83, 0xd0, 0xe6, 0xf6, 0x34, 0xd2, 0xf1, 0x28, 0x9c, + 0x3c, 0x43, 0x49, 0x0f, 0x05, 0xb4, 0x2e, 0x52, 0x94, 0x44, 0x7c, 0xac, 0xe2, 0x01, 0xa2, 0x5c, + 0x21, 0x85, 0xf8, 0x49, 0xf5, 0xd3, 0x6a, 0x98, 0x00, 0x41, 0x0a, 0xb9, 0x8b, 0x59, 0xe4, 0xbe, + 0x89, 0xd3, 0xab, 0xb7, 0xc2, 0xe9, 0x1b, 0x1d, 0xa7, 0xf4, 0x23, 0x1d, 0x87, 0x3c, 0x81, 0xb2, + 0xc6, 0x67, 0xf3, 0x11, 0x3e, 0x82, 0x5a, 0x0a, 0xc7, 0x69, 0x2c, 0xb3, 0x7e, 0x0f, 0xd5, 0xc4, + 0x5c, 0x96, 0xd7, 0xbc, 0x9f, 0xd5, 0xa9, 0x22, 0xc8, 0x43, 0x80, 0x48, 0x75, 0xab, 0x81, 0xe7, + 0x6a, 0xa8, 0xad, 0x6a, 0x4e, 0xd7, 0x95, 0xf9, 0x66, 0x57, 0xa1, 0xc7, 0x6d, 0x21, 0x2b, 0xad, + 0x80, 0x50, 0x96, 0xe2, 0x58, 0xff, 0x2d, 0x42, 0xb9, 0xcf, 0x46, 0x07, 0xb6, 0xb0, 0xb1, 0x2a, + 0x6d, 0xdf, 0x1b, 0xb2, 0x48, 0x74, 0x5d, 0xbd, 0x4b, 0x8a, 0x83, 0x4d, 0x8d, 0xbd, 0xd5, 0x78, + 0x28, 0x97, 0x08, 0xfa, 0x76, 0x34, 0x46, 0xbf, 0x75, 0x8a, 0x6b, 0x09, 0xc6, 0x21, 0x0f, 0x86, + 0xde, 0x84, 0xc5, 0xb9, 0x4d, 0xe8, 0xb8, 0x2d, 0xae, 0xce, 0xdb, 0x62, 0x13, 0x2a, 0xee, 0x4c, + 0x47, 0x27, 0xb3, 0xb6, 0x4a, 0x13, 0x7a, 0xe1, 0x2a, 0xca, 0x1f, 0x72, 0x15, 0x95, 0x1f, 0xbb, + 0x8a, 0x67, 0x70, 0xd7, 0xb1, 0x27, 0xce, 0x20, 0x64, 0xdc, 0x61, 0xa1, 0x98, 0xd9, 0x93, 0x01, + 0x9e, 0x09, 0xb0, 0x7c, 0x88, 0x94, 0xf5, 0x12, 0xd1, 0x91, 0x3c, 0xe1, 0xed, 0x2e, 0x4f, 0x86, + 0x3f, 0x9c, 0x4d, 0x26, 0xbd, 0x38, 0x19, 0x8f, 0x51, 0x57, 0x85, 0x7f, 0xee, 0xb9, 0x2c, 0xd0, + 0x12, 0x9a, 0x51, 0x23, 0x5f, 0x41, 0x23, 0x4d, 0xb7, 0x4d, 0xeb, 0x87, 0xec, 0xb2, 0x7a, 0x37, + 0x0d, 0xb7, 0xcd, 0x4f, 0x6e, 0x65, 0xb8, 0x4d, 0x76, 0x81, 0x44, 0x6c, 0x34, 0x65, 0xbe, 0x2e, + 0x3a, 0x26, 0x18, 0x8f, 0xcc, 0x27, 0x98, 0x38, 0xa2, 0x26, 0x05, 0x36, 0xea, 0x25, 0x12, 0xba, + 0xa1, 0xb5, 0xe7, 0x2c, 0xd2, 0x02, 0xf2, 0x2a, 0xe0, 0x0e, 0x4b, 0x06, 0x27, 0x4f, 0x76, 0xce, + 0xcf, 0x54, 0x0a, 0x17, 0x25, 0xd6, 0x36, 0x34, 0x32, 0x3e, 0xe5, 0x4b, 0x1a, 0xf2, 0x40, 0x81, + 0x56, 0x91, 0xe2, 0x9a, 0xac, 0x41, 0x5e, 0x04, 0xf8, 0xdc, 0x8a, 0x34, 0x2f, 0x02, 0xeb, 0x5f, + 0xab, 0x50, 0x4f, 0x9f, 0x43, 0x1a, 0xf9, 0xf6, 0x94, 0xe1, 0x50, 0x53, 0xa5, 0xb8, 0x96, 0x55, + 0xf2, 0xce, 0x73, 0xc5, 0xd8, 0xdc, 0xc0, 0xd7, 0xa4, 0x08, 0x39, 0x77, 0x8c, 0x99, 0x37, 0x1a, + 0x0b, 0x93, 0x20, 0x5b, 0x53, 0x12, 0x07, 0x2e, 0x3c, 0x09, 0x4f, 0xcc, 0xbc, 0x83, 0x82, 0x98, + 0x94, 0x4f, 0x75, 0x18, 0x46, 0xe6, 0x5d, 0xd5, 0x14, 0x86, 0x61, 0x44, 0x9e, 0x41, 0x69, 0x18, + 0xf0, 0xa9, 0x2d, 0xcc, 0x4d, 0x1c, 0xbd, 0xcc, 0x85, 0xc4, 0xb6, 0x5e, 0xa1, 0x9c, 0x6a, 0x3d, + 0xb9, 0xeb, 0x30, 0x8c, 0x0e, 0x98, 0x6f, 0x6e, 0xa1, 0x1b, 0x4d, 0x91, 0x6d, 0x28, 0xeb, 0x92, + 0x30, 0xef, 0xa1, 0xab, 0xfb, 0x8b, 0xae, 0xe2, 0xbb, 0x8a, 0x35, 0x65, 0x40, 0xa3, 0x20, 0x34, + 0x4d, 0x0c, 0x53, 0x2e, 0xc9, 0x0b, 0x28, 0x33, 0x5f, 0x01, 0xe9, 0x7d, 0x74, 0xf3, 0x60, 0xd1, + 0x0d, 0x12, 0xfb, 0x81, 0xcb, 0x1c, 0x1a, 0x2b, 0xe3, 0x38, 0x15, 0x4c, 0x02, 0x7e, 0xc0, 0x42, + 0x31, 0x36, 0x9b, 0xe8, 0x30, 0xc5, 0x21, 0x87, 0x50, 0x77, 0xc6, 0x3c, 0x98, 0xda, 0xea, 0x38, + 0xe6, 0x47, 0xe8, 0xfc, 0x93, 0x45, 0xe7, 0xfb, 0xa8, 0xd5, 0x9f, 0x5d, 0x44, 0xf6, 0x34, 0x9c, + 0x78, 0xfe, 0x88, 0x66, 0x0c, 0x65, 0x76, 0xdf, 0xce, 0x6c, 0x6c, 0xe0, 0x0f, 0x30, 0x01, 0x31, + 0x69, 0x3d, 0x84, 0x92, 0xd6, 0x01, 0x28, 0x1d, 0xf7, 0x3a, 0x87, 0x67, 0x7d, 0x63, 0x85, 0x94, + 0xa1, 0x70, 0xdc, 0xdb, 0x31, 0x72, 0xd6, 0x1f, 0xa0, 0x1c, 0xdf, 0xf1, 0x1d, 0x58, 0xef, 0x9c, + 0xec, 0x9f, 0x1e, 0x74, 0xe8, 0xe0, 0xa0, 0xf3, 0x6a, 0xf7, 0xcd, 0x6b, 0x39, 0x8d, 0x6e, 0x40, + 0xe3, 0xa8, 0xfd, 0x62, 0x67, 0xb0, 0xb7, 0xdb, 0xef, 0xbc, 0xee, 0x9e, 0x74, 0x8c, 0x1c, 0x69, + 0x40, 0x15, 0x59, 0xc7, 0xbb, 0xdd, 0x13, 0x23, 0x9f, 0x90, 0x47, 0xdd, 0xc3, 0x23, 0xa3, 0x40, + 0xee, 0xc3, 0x26, 0x92, 0xfb, 0xa7, 0x27, 0xfd, 0x33, 0xba, 0xdb, 0x3d, 0xe9, 0x1c, 0x28, 0x51, + 0xd1, 0x6a, 0x03, 0xcc, 0x93, 0x44, 0x2a, 0x50, 0x94, 0x8a, 0xc6, 0x8a, 0x5e, 0x3d, 0x37, 0x72, + 0x32, 0xac, 0xf3, 0xde, 0xd7, 0x46, 0x5e, 0x2d, 0x5e, 0x1a, 0x05, 0x6b, 0x1f, 0x36, 0x16, 0xce, + 0x4e, 0xd6, 0x00, 0xf6, 0x8f, 0xe8, 0xe9, 0xf1, 0xee, 0x60, 0xa7, 0xfd, 0xcc, 0x58, 0xc9, 0xd0, + 0x6d, 0x23, 0x97, 0xa6, 0x77, 0x76, 0x8c, 0xbc, 0xf5, 0x16, 0x36, 0xe3, 0x4f, 0x0e, 0xe6, 0xf6, + 0x55, 0x49, 0x21, 0x0e, 0x1b, 0x50, 0x98, 0xf1, 0x49, 0xdc, 0x9d, 0x67, 0x7c, 0x82, 0x63, 0x33, + 0x8e, 0x9f, 0x1a, 0x7c, 0x35, 0x45, 0x5a, 0x70, 0xe7, 0x06, 0x6c, 0x0d, 0xa4, 0xa5, 0x9a, 0xad, + 0x37, 0xc2, 0x0c, 0x6c, 0xbd, 0xe1, 0x13, 0xeb, 0x37, 0xd0, 0x48, 0xb6, 0xc4, 0xad, 0x5e, 0x40, + 0x45, 0x17, 0x73, 0x3c, 0x8d, 0x34, 0x55, 0xa7, 0x5d, 0x16, 0x18, 0x4d, 0x74, 0x17, 0xbf, 0x6f, + 0xac, 0xbf, 0xe4, 0x60, 0x3d, 0xb1, 0xa2, 0x2c, 0x9a, 0x4d, 0x44, 0xdc, 0x30, 0x72, 0xf3, 0x86, + 0xb1, 0x05, 0xab, 0x8c, 0xf3, 0x80, 0xab, 0x46, 0x75, 0xb4, 0x42, 0x15, 0x49, 0x9e, 0x42, 0xd1, + 0xb5, 0x85, 0xad, 0x1b, 0x37, 0xc9, 0xc6, 0x20, 0xf7, 0x3e, 0x5a, 0xa1, 0xa8, 0x41, 0x3e, 0x87, + 0x62, 0xea, 0x43, 0x66, 0x53, 0x21, 0xef, 0x8d, 0x29, 0x83, 0xa2, 0xca, 0x5e, 0x05, 0x4a, 0x1c, + 0x03, 0xb1, 0xfe, 0x08, 0xeb, 0x94, 0x8d, 0xbc, 0x48, 0xb0, 0xe4, 0xdb, 0x6d, 0x0b, 0x4a, 0x11, + 0x73, 0x38, 0x8b, 0xbf, 0x58, 0x34, 0x25, 0x1b, 0x92, 0x1e, 0xa9, 0xaf, 0x75, 0xb2, 0x13, 0xfa, + 0x43, 0xbf, 0xe1, 0xfe, 0x94, 0x83, 0xc6, 0x49, 0x20, 0xbc, 0xe1, 0xb5, 0x4e, 0xe6, 0x92, 0x1b, + 0xfe, 0x0c, 0xca, 0x91, 0x6a, 0xc3, 0xda, 0x6b, 0x3d, 0x06, 0x5e, 0xcc, 0x7c, 0x2c, 0x94, 0x61, + 0x0b, 0x3b, 0xba, 0xec, 0xba, 0x98, 0x80, 0x02, 0xd5, 0x54, 0xa6, 0xeb, 0x6e, 0x64, 0xbb, 0xee, + 0xb7, 0xc5, 0x4a, 0xde, 0x28, 0x7c, 0x5b, 0xac, 0x3c, 0x36, 0x2c, 0xeb, 0xaf, 0x79, 0xa8, 0xa7, + 0xc7, 0x28, 0xf9, 0xdd, 0xc2, 0x99, 0xe3, 0x85, 0x1e, 0xf3, 0x85, 0xee, 0xf9, 0x73, 0x86, 0x9c, + 0x2e, 0x86, 0xb6, 0xc3, 0x06, 0xf3, 0xd9, 0xb0, 0x4e, 0xab, 0x92, 0x73, 0x2e, 0x19, 0xe4, 0x3e, + 0x54, 0xde, 0x79, 0xfe, 0x20, 0xe4, 0xc1, 0x85, 0x9e, 0x01, 0xca, 0xef, 0x3c, 0xbf, 0xc7, 0x83, + 0x0b, 0xf9, 0x34, 0x13, 0x37, 0x03, 0x6e, 0xfb, 0xae, 0xea, 0xaa, 0x6a, 0x22, 0xd8, 0x48, 0x44, + 0xd4, 0xf6, 0x5d, 0x6c, 0xaa, 0x04, 0x8a, 0x11, 0x63, 0xae, 0x9e, 0x0d, 0x70, 0x4d, 0x3e, 0x07, + 0x63, 0x3e, 0xaa, 0x0c, 0x2e, 0x26, 0x81, 0x73, 0x89, 0x43, 0x42, 0x9d, 0xae, 0xcf, 0xf9, 0x7b, + 0x92, 0x4d, 0x8e, 0x60, 0x23, 0xa5, 0xaa, 0x67, 0x47, 0x35, 0x30, 0x7c, 0x94, 0x9a, 0x1d, 0x3b, + 0x89, 0x8e, 0x9e, 0x22, 0x53, 0x1b, 0x28, 0x8e, 0xd5, 0x05, 0xa2, 0x74, 0xfb, 0xcc, 0x77, 0x19, + 0xd7, 0x69, 0x7a, 0x0c, 0xf5, 0x08, 0xe9, 0x81, 0x1f, 0xf8, 0x0e, 0xd3, 0x1f, 0x0b, 0x35, 0xc5, + 0x3b, 0x91, 0xac, 0x25, 0x35, 0xf1, 0x3d, 0x6c, 0x2d, 0xdf, 0x96, 0x3c, 0x81, 0x35, 0x87, 0x33, + 0x15, 0x2c, 0x0f, 0x66, 0xbe, 0xab, 0x8b, 0xa4, 0x11, 0x73, 0xa9, 0x64, 0x92, 0x97, 0x70, 0x3f, + 0xab, 0xa6, 0x92, 0xa0, 0x52, 0xa9, 0x36, 0xda, 0xca, 0x58, 0x60, 0x32, 0x64, 0x3e, 0xad, 0x7f, + 0xe4, 0xa1, 0xdc, 0xb3, 0xaf, 0xf1, 0xb9, 0x2d, 0x0c, 0xd5, 0xb9, 0xdb, 0x0d, 0xd5, 0x58, 0x23, + 0xf2, 0x80, 0x7a, 0x2f, 0x4d, 0x2d, 0x4f, 0x76, 0xe1, 0x03, 0x92, 0x4d, 0xba, 0x70, 0x57, 0x47, + 0xa6, 0xb3, 0xab, 0x9d, 0x15, 0x11, 0x8b, 0xee, 0xa5, 0x9c, 0xa5, 0x6f, 0x83, 0x12, 0xb1, 0x78, + 0x43, 0xcf, 0x61, 0x8d, 0x5d, 0x85, 0xcc, 0x11, 0xcc, 0x1d, 0xe0, 0xa0, 0xaf, 0x47, 0xf7, 0x9b, + 0x5f, 0x01, 0x8d, 0x58, 0x0b, 0x59, 0xed, 0x7f, 0xe7, 0xa0, 0x9e, 0xc6, 0x0f, 0xb2, 0x07, 0xeb, + 0x87, 0x4c, 0x64, 0x58, 0xe6, 0x02, 0xca, 0x68, 0x14, 0x69, 0x2e, 0xc7, 0x1f, 0xf2, 0x3b, 0xd8, + 0x5c, 0xfa, 0x43, 0x89, 0xa8, 0x2f, 0xfa, 0xf7, 0xfd, 0xbb, 0x6a, 0x5a, 0xef, 0x53, 0x51, 0xff, + 0xa3, 0xc8, 0xa7, 0x50, 0xec, 0xc9, 0x96, 0xa3, 0xfe, 0xe3, 0xc4, 0x3f, 0xcb, 0x9a, 0x59, 0xb2, + 0x7d, 0x02, 0x70, 0x36, 0xff, 0xb2, 0xfa, 0x15, 0x90, 0x18, 0x03, 0x53, 0xdc, 0xbb, 0x68, 0x72, + 0x03, 0x1c, 0x9b, 0x0a, 0x80, 0x33, 0x98, 0xf5, 0x2c, 0xb7, 0x57, 0xfe, 0xed, 0x6a, 0xeb, 0x4b, + 0x9f, 0x89, 0x8b, 0x12, 0xfe, 0xac, 0xdb, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x6f, 0xbf, + 0xc3, 0x6f, 0xc0, 0x13, 0x00, 0x00, } diff --git a/net/lp_rpc.proto b/net/lp_rpc.proto index bd3e6f1ef..4cd940d6a 100644 --- a/net/lp_rpc.proto +++ b/net/lp_rpc.proto @@ -112,9 +112,10 @@ message Capabilities { Constraints constraints = 5; - // Non-binary general constraints. + // Non-binary constraints. message Constraints { string minVersion = 1; + map PerCapability = 2; } // Non-binary capability constraints, such as supported ranges. @@ -126,7 +127,7 @@ message Capabilities { map models = 1; } - map capabilityConstraints = 6; + } // The orchestrator sends this in response to `GetOrchestrator`, containing diff --git a/server/ai_session.go b/server/ai_session.go index 3a6c28207..2e8c62f7a 100644 --- a/server/ai_session.go +++ b/server/ai_session.go @@ -259,7 +259,7 @@ func (sel *AISessionSelector) Refresh(ctx context.Context) error { var coldSessions []*BroadcastSession for _, sess := range sessions { // If the constraints are missing for this capability skip this session - constraints, ok := sess.OrchestratorInfo.Capabilities.CapabilityConstraints[uint32(sel.cap)] + constraints, ok := sess.OrchestratorInfo.Capabilities.Constraints.PerCapability[uint32(sel.cap)] if !ok { continue } @@ -288,7 +288,7 @@ func (sel *AISessionSelector) Refresh(ctx context.Context) error { func (sel *AISessionSelector) getSessions(ctx context.Context) ([]*BroadcastSession, error) { // No warm constraints applied here because we don't want to filter out orchs based on warm criteria at discovery time // Instead, we want all orchs that support the model and then will filter for orchs that have a warm model separately - capabilityConstraints := map[core.Capability]*core.PerCapabilityConstraints{ + capabilityConstraints := core.PerCapabilityConstraints{ sel.cap: { Models: map[string]*core.ModelConstraint{ sel.modelID: { @@ -297,7 +297,8 @@ func (sel *AISessionSelector) getSessions(ctx context.Context) ([]*BroadcastSess }, }, } - caps := core.NewCapabilitiesWithConstraints(append(core.DefaultCapabilities(), sel.cap), nil, core.Constraints{}, capabilityConstraints) + caps := core.NewCapabilities(append(core.DefaultCapabilities(), sel.cap), nil) + caps.SetPerCapabilityConstraints(capabilityConstraints) caps.SetMinVersionConstraint(sel.node.Capabilities.MinVersionConstraint()) // Set numOrchs to the pool size so that discovery tries to find maximum # of compatible orchs within a timeout From 35927af2b2759f59ff7bfe58fd4f203574d26ca5 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Sat, 10 Aug 2024 09:11:37 +0200 Subject: [PATCH 163/203] chore: update lpms dependency to master branch (#3125) This commit updates the lpms dependency to the master branch now the ai-video related features are merged in (see https://github.com/livepeer/lpms/pull/415). --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1a59fc36f..929018f6e 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/livepeer/ai-worker v0.1.0 github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 - github.com/livepeer/lpms v0.0.0-20240731140137-28406cf8bc78 + github.com/livepeer/lpms v0.0.0-20240809194342-c3330413a4a0 github.com/livepeer/m3u8 v0.11.1 github.com/mattn/go-sqlite3 v1.14.18 github.com/oapi-codegen/nethttp-middleware v1.0.1 diff --git a/go.sum b/go.sum index 2e9eacc71..07d346377 100644 --- a/go.sum +++ b/go.sum @@ -631,8 +631,8 @@ github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cO github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded/go.mod h1:xkDdm+akniYxVT9KW1Y2Y7Hso6aW+rZObz3nrA9yTHw= github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 h1:4oH3NqV0NvcdS44Ld3zK2tO8IUiNozIggm74yobQeZg= github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18/go.mod h1:Jpf4jHK+fbWioBHRDRM1WadNT1qmY27g2YicTdO0Rtc= -github.com/livepeer/lpms v0.0.0-20240731140137-28406cf8bc78 h1:J026gPdu533qY+KNEgWaSiu3dExW+DB4UyE8+PAEKvg= -github.com/livepeer/lpms v0.0.0-20240731140137-28406cf8bc78/go.mod h1:z5ROP1l5OzAKSoqVRLc34MjUdueil6wHSecQYV7llIw= +github.com/livepeer/lpms v0.0.0-20240809194342-c3330413a4a0 h1:t+n2QuNllKouI3bSygepyKUBMc3+qZ8YyW6NzgY9+Os= +github.com/livepeer/lpms v0.0.0-20240809194342-c3330413a4a0/go.mod h1:z5ROP1l5OzAKSoqVRLc34MjUdueil6wHSecQYV7llIw= github.com/livepeer/m3u8 v0.11.1 h1:VkUJzfNTyjy9mqsgp5JPvouwna8wGZMvd/gAfT5FinU= github.com/livepeer/m3u8 v0.11.1/go.mod h1:IUqAtwWPAG2CblfQa4SVzTQoDcEMPyfNOaBSxqHMS04= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= From 27f1bb0ea382d1898cc364d718e47db99e76c028 Mon Sep 17 00:00:00 2001 From: PSchroedl Date: Wed, 14 Aug 2024 11:39:07 -0700 Subject: [PATCH 164/203] Ai 303/pricing multiple images t2i i2i (#3126) This commit updates the orchestrator to consider the number of images when calculating DebitFees. It also corrects the logic for the pricePerAIUnit metric and ensures the gateway sets a default number of images when not provided, preventing nil errors in the orchestrator. --------- Co-authored-by: Rick Staa --- server/ai_http.go | 15 ++++++++++-- server/ai_process.go | 54 ++++++++++++++++++++++++++++---------------- 2 files changed, 47 insertions(+), 22 deletions(-) diff --git a/server/ai_http.go b/server/ai_http.go index eba099df3..6b007e0c1 100644 --- a/server/ai_http.go +++ b/server/ai_http.go @@ -195,8 +195,13 @@ func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request if v.Width != nil { width = int64(*v.Width) } + // NOTE: Should be enforced by the gateway, added for backwards compatibility. + numImages := int64(1) + if v.NumImagesPerPrompt != nil { + numImages = int64(*v.NumImagesPerPrompt) + } - outPixels = height * width + outPixels = height * width * numImages case worker.ImageToImageMultipartRequestBody: pipeline = "image-to-image" cap = core.Capability_ImageToImage @@ -215,7 +220,13 @@ func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request respondWithError(w, err.Error(), http.StatusBadRequest) return } - outPixels = int64(config.Height) * int64(config.Width) + // NOTE: Should be enforced by the gateway, added for backwards compatibility. + numImages := int64(1) + if v.NumImagesPerPrompt != nil { + numImages = int64(*v.NumImagesPerPrompt) + } + + outPixels = int64(config.Height) * int64(config.Width) * numImages case worker.UpscaleMultipartRequestBody: pipeline = "upscale" cap = core.Capability_Upscale diff --git a/server/ai_process.go b/server/ai_process.go index d28658221..a8e884f68 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -60,12 +60,8 @@ func CalculateTextToImageLatencyScore(took time.Duration, req worker.TextToImage return 0 } - // TODO: Default values for the number of images and inference steps are currently hardcoded. + // TODO: Default values for the number of inference steps is currently hardcoded. // These should be managed by the nethttpmiddleware. Refer to issue LIV-412 for more details. - numImages := float64(1) - if req.NumImagesPerPrompt != nil { - numImages = math.Max(1, float64(*req.NumImagesPerPrompt)) - } numInferenceSteps := float64(50) if req.NumInferenceSteps != nil { numInferenceSteps = math.Max(1, float64(*req.NumInferenceSteps)) @@ -75,7 +71,7 @@ func CalculateTextToImageLatencyScore(took time.Duration, req worker.TextToImage numInferenceSteps = math.Max(1, core.ParseStepsFromModelID(req.ModelId, 8)) } - return took.Seconds() / float64(outPixels) / (numImages * numInferenceSteps) + return took.Seconds() / float64(outPixels) / numInferenceSteps } func processTextToImage(ctx context.Context, params aiRequestParams, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) { @@ -128,7 +124,17 @@ func submitTextToImage(ctx context.Context, params aiRequestParams, sess *AISess *req.Width = 512 } - outPixels := int64(*req.Height) * int64(*req.Width) + // TODO: Default values for the number of images is currently hardcoded. + // These should be managed by the nethttpmiddleware. Refer to issue LIV-412 for more details. + defaultNumImages := 1 + if req.NumImagesPerPrompt == nil { + req.NumImagesPerPrompt = &defaultNumImages + } else { + *req.NumImagesPerPrompt = int(math.Max(1, float64(*req.NumImagesPerPrompt))) + } + + outPixels := int64(*req.Height) * int64(*req.Width) * int64(*req.NumImagesPerPrompt) + setHeaders, balUpdate, err := prepareAIPayment(ctx, sess, outPixels) if err != nil { if monitor.Enabled { @@ -141,6 +147,10 @@ func submitTextToImage(ctx context.Context, params aiRequestParams, sess *AISess start := time.Now() resp, err := client.TextToImageWithResponse(ctx, req, setHeaders) took := time.Since(start) + + // TODO: Refine this rough estimate in future iterations. + sess.LatencyScore = CalculateTextToImageLatencyScore(took, req, outPixels) + if err != nil { if monitor.Enabled { monitor.AIRequestError(err.Error(), "text-to-image", *req.ModelId, sess.OrchestratorInfo) @@ -158,9 +168,6 @@ func submitTextToImage(ctx context.Context, params aiRequestParams, sess *AISess balUpdate.Status = ReceivedChange } - // TODO: Refine this rough estimate in future iterations. - sess.LatencyScore = CalculateTextToImageLatencyScore(took, req, outPixels) - if monitor.Enabled { var pricePerAIUnit float64 if priceInfo := sess.OrchestratorInfo.GetPriceInfo(); priceInfo != nil && priceInfo.PixelsPerUnit != 0 { @@ -179,12 +186,8 @@ func CalculateImageToImageLatencyScore(took time.Duration, req worker.ImageToIma return 0 } - // TODO: Default values for the number of images and inference steps are currently hardcoded. + // TODO: Default values for the number of inference steps is currently hardcoded. // These should be managed by the nethttpmiddleware. Refer to issue LIV-412 for more details. - numImages := float64(1) - if req.NumImagesPerPrompt != nil { - numImages = math.Max(1, float64(*req.NumImagesPerPrompt)) - } numInferenceSteps := float64(100) if req.NumInferenceSteps != nil { numInferenceSteps = math.Max(1, float64(*req.NumInferenceSteps)) @@ -194,7 +197,7 @@ func CalculateImageToImageLatencyScore(took time.Duration, req worker.ImageToIma numInferenceSteps = math.Max(1, core.ParseStepsFromModelID(req.ModelId, 8)) } - return took.Seconds() / float64(outPixels) / (numImages * numInferenceSteps) + return took.Seconds() / float64(outPixels) / numInferenceSteps } func processImageToImage(ctx context.Context, params aiRequestParams, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { @@ -229,6 +232,15 @@ func processImageToImage(ctx context.Context, params aiRequestParams, req worker } func submitImageToImage(ctx context.Context, params aiRequestParams, sess *AISession, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { + // TODO: Default values for the number of images is currently hardcoded. + // These should be managed by the nethttpmiddleware. Refer to issue LIV-412 for more details. + defaultNumImages := 1 + if req.NumImagesPerPrompt == nil { + req.NumImagesPerPrompt = &defaultNumImages + } else { + *req.NumImagesPerPrompt = int(math.Max(1, float64(*req.NumImagesPerPrompt))) + } + var buf bytes.Buffer mw, err := worker.NewImageToImageMultipartWriter(&buf, req) if err != nil { @@ -260,7 +272,8 @@ func submitImageToImage(ctx context.Context, params aiRequestParams, sess *AISes } return nil, err } - outPixels := int64(config.Height) * int64(config.Width) + + outPixels := int64(config.Height) * int64(config.Width) * int64(*req.NumImagesPerPrompt) setHeaders, balUpdate, err := prepareAIPayment(ctx, sess, outPixels) if err != nil { @@ -274,6 +287,10 @@ func submitImageToImage(ctx context.Context, params aiRequestParams, sess *AISes start := time.Now() resp, err := client.ImageToImageWithBodyWithResponse(ctx, mw.FormDataContentType(), &buf, setHeaders) took := time.Since(start) + + // TODO: Refine this rough estimate in future iterations. + sess.LatencyScore = CalculateImageToImageLatencyScore(took, req, outPixels) + if err != nil { if monitor.Enabled { monitor.AIRequestError(err.Error(), "image-to-image", *req.ModelId, sess.OrchestratorInfo) @@ -291,9 +308,6 @@ func submitImageToImage(ctx context.Context, params aiRequestParams, sess *AISes balUpdate.Status = ReceivedChange } - // TODO: Refine this rough estimate in future iterations. - sess.LatencyScore = CalculateImageToImageLatencyScore(took, req, outPixels) - if monitor.Enabled { var pricePerAIUnit float64 if priceInfo := sess.OrchestratorInfo.GetPriceInfo(); priceInfo != nil && priceInfo.PixelsPerUnit != 0 { From 76410694fabee350c93fc0ce1d986a802fb84084 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 15 Aug 2024 13:40:07 +0200 Subject: [PATCH 165/203] fix(ai): fix unsupported capability nil error (#3132) This commit fixes a `nil` error that was introduced during the rebase which was thrown if the `GetOrchestrator` endpoint was called for a capability that a orchestrator does not support. --- core/livepeernode.go | 6 +++++- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/core/livepeernode.go b/core/livepeernode.go index 0d726ce65..f5d87ccfa 100644 --- a/core/livepeernode.go +++ b/core/livepeernode.go @@ -236,7 +236,11 @@ func (n *LivepeerNode) GetBasePriceForCap(b_eth_addr string, cap Capability, mod return nil } - return prices.PriceForModelID(cap, modelID).Value() + if price := prices.PriceForModelID(cap, modelID); price != nil { + return price.Value() + } + + return nil } // SetMaxFaceValue sets the faceValue upper limit for tickets received diff --git a/go.mod b/go.mod index 929018f6e..4700e89b2 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/golang/protobuf v1.5.4 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.1.0 + github.com/livepeer/ai-worker v0.1.2 github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240809194342-c3330413a4a0 diff --git a/go.sum b/go.sum index 07d346377..5ddc8d829 100644 --- a/go.sum +++ b/go.sum @@ -623,8 +623,8 @@ github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4n github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/livepeer/ai-worker v0.1.0 h1:SJBZuxeK0vEzJPBzf5osdgVCxHYZt7ZKR2CvZ7Q7iog= -github.com/livepeer/ai-worker v0.1.0/go.mod h1:Xlnb0nFG2VsGeMG9hZmReVQXeFt0Dv28ODiUT2ooyLE= +github.com/livepeer/ai-worker v0.1.2 h1:I73J4zJYad95QE1JFSrqrjKKCTqLHypDcoPq/zZM5aw= +github.com/livepeer/ai-worker v0.1.2/go.mod h1:Xlnb0nFG2VsGeMG9hZmReVQXeFt0Dv28ODiUT2ooyLE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= From 423d6c825ff7a35062cb559077ddaf97fb701500 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 15 Aug 2024 14:49:15 +0200 Subject: [PATCH 166/203] refactor(ai): fixes a small merge conflict This commit fixes a small merge conflict that was introduced in the last merge. --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 3110813d1..ee8a0c2ba 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -32,7 +32,7 @@ jobs: # - GOOS: linux # GOARCH: arm64 - # runner: ubuntu-20.04 + # container: ubuntu-20.04 # type: cpu - GOOS: linux From c497cf8d5f3b23658c57bc3be2eb50020854c8e0 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 15 Aug 2024 15:40:25 +0200 Subject: [PATCH 167/203] ci(ai): disable windows binary build This commit temporarily disables the windows binary build since it seems to fail after the rebase (see https://github.com/livepeer/go-livepeer/actions/runs/10404655818/job/28813594707). --- .github/workflows/build.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index ee8a0c2ba..d5223daa1 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -45,10 +45,10 @@ jobs: # container: livepeerci/cuda:12.0.0-cudnn8-devel-ubuntu20.04 # type: gpu - - GOOS: windows - GOARCH: amd64 - container: ubuntu:22.04 - type: cpu + # - GOOS: windows + # GOARCH: amd64 + # container: ubuntu:22.04 + # type: cpu steps: - name: Setup ubuntu container From 0b6f600c6bb80a4414c1b0ca32dfb17ecc0ad99b Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 15 Aug 2024 16:25:59 +0200 Subject: [PATCH 168/203] refactor: remove unused build script This commit removes a unused build script that should have been removed during a rebase. --- upload_build.sh | 155 ------------------------------------------------ 1 file changed, 155 deletions(-) delete mode 100755 upload_build.sh diff --git a/upload_build.sh b/upload_build.sh deleted file mode 100755 index 6cd4df884..000000000 --- a/upload_build.sh +++ /dev/null @@ -1,155 +0,0 @@ -#!/bin/bash - -# CI script for uploading builds. - -set -e -set -o nounset - -BASE_DIR="$(realpath $(dirname "$0"))" - -cd "$BASE_DIR" -RELEASES_DIR="${BASE_DIR}/${RELEASES_DIR:-releases}/" - -mkdir -p "$RELEASES_DIR" - -if [[ "${GOOS:-}" != "" ]]; then - PLATFORM="$GOOS" -elif [[ $(uname) == *"MSYS"* ]]; then - PLATFORM="windows" -else - PLATFORM=$(uname | tr '[:upper:]' '[:lower:]') -fi - -EXT="" -if [[ "$PLATFORM" == "windows" ]]; then - EXT=".exe" -fi -if [[ "$PLATFORM" != "linux" ]] && [[ "$PLATFORM" != "darwin" ]] && [[ "$PLATFORM" != "windows" ]]; then - echo "Unknown/unsupported platform: $PLATFORM" - exit 1 -fi - -if [[ -n "${RELEASE_TAG:-}" ]]; then - PLATFORM="$PLATFORM-$RELEASE_TAG" -fi - -ARCH="$(uname -m)" - -if [[ "${GOARCH:-}" != "" ]]; then - ARCH="$GOARCH" -fi -if [[ "$ARCH" == "x86_64" ]]; then - ARCH="amd64" -fi -if [[ "$ARCH" == "aarch64" ]]; then - ARCH="arm64" -fi -if [[ "$ARCH" != "amd64" ]] && [[ "$ARCH" != "arm64" ]]; then - echo "Unknown/unsupported architecture: $ARCH" - exit 1 -fi - -BASE="livepeer-$PLATFORM-$ARCH" -BRANCH="${TRAVIS_BRANCH:-unknown}" -if [[ "${GHA_REF:-}" != "" ]]; then - BRANCH="$(echo $GHA_REF | sed 's:refs/heads/::')" -fi -VERSION="$(./print_version.sh)" -if echo $VERSION | grep dirty; then - echo "Error: git state dirty, refusing to upload build" - git diff | cat - git status - exit 1 -fi - -# If we want to build with branch --> network support for any other networks, add them here! -NETWORK_BRANCHES="rinkeby mainnet" -# If the binaries are built off a network branch then the resource path should include the network branch name i.e. X.Y.Z/rinkeby or X.Y.Z/mainnet -# If the binaries are not built off a network then the resource path should only include the version i.e. X.Y.Z -VERSION_AND_NETWORK=$VERSION -for networkBranch in $NETWORK_BRANCHES; do - if [[ $BRANCH == "$networkBranch" ]]; then - VERSION_AND_NETWORK="$VERSION/$BRANCH" - fi -done - -NODE="./livepeer${EXT}" -CLI="./livepeer_cli${EXT}" -BENCH="./livepeer_bench${EXT}" -ROUTER="./livepeer_router${EXT}" - -mkdir -p "${BASE_DIR}/$BASE" - -# Optionally step into build directory, if set anywhere -cd "${GO_BUILD_DIR:-./}" -cp "$NODE" "$CLI" "$BENCH" "$ROUTER" "${BASE_DIR}/$BASE" -cd - - -# do a basic upload so we know if stuff's working prior to doing everything else -if [[ $PLATFORM == "windows" ]]; then - FILE="$BASE.zip" - zip -r "${RELEASES_DIR}/$FILE" ./$BASE -else - FILE="$BASE.tar.gz" - tar -czvf "${RELEASES_DIR}/$FILE" ./$BASE -fi - -cd "$RELEASES_DIR" - -FILE_SHA256=$(shasum -a 256 ${FILE}) - -if [[ "${GCLOUD_KEY:-}" == "" ]]; then - echo "GCLOUD_KEY not found, not uploading to Google Cloud." - exit 0 -fi - -# https://stackoverflow.com/a/44751929/990590 -BUCKET="build.livepeer.live" -PROJECT="go-livepeer" -BUCKET_PATH="${PROJECT}/${VERSION_AND_NETWORK}/${FILE}" -resource="/${BUCKET}/${BUCKET_PATH}" -contentType="application/x-compressed-tar" -dateValue="$(date -R)" -stringToSign="PUT\n\n${contentType}\n${dateValue}\n${resource}" -signature="$(echo -en ${stringToSign} | openssl sha1 -hmac ${GCLOUD_SECRET} -binary | base64)" -fullUrl="https://storage.googleapis.com${resource}" - -# Create temporary latest url for the 'ai-video' branch -# TODO: Once 'ai-video' is merged into the main branch, this section should be removed. -BRANCH="ai-video" -if [[ $BRANCH == "ai-video" ]]; then - BUCKET_PATH_LATEST="${PROJECT}/$BRANCH/latest/${FILE}" - resource_latest="/${BUCKET}/${BUCKET_PATH_LATEST}" - stringToSignLatest="PUT\n\n${contentType}\n${dateValue}\n${resource_latest}" - signatureLatest="$(echo -en ${stringToSignLatest} | openssl sha1 -hmac ${GCLOUD_SECRET} -binary | base64)" - fullUrlLatest="https://storage.googleapis.com${resource_latest}" - - # Upload the latest ai-video build - curl -X PUT -T "${FILE}" \ - -H "Host: storage.googleapis.com" \ - -H "Date: ${dateValue}" \ - -H "Content-Type: ${contentType}" \ - -H "Authorization: AWS ${GCLOUD_KEY}:${signatureLatest}" \ - $fullUrlLatest -fi - -# Failsafe - don't overwrite existing uploads! -if curl --head --fail $fullUrl 2>/dev/null; then - echo "$fullUrl already exists, not overwriting!" - exit 0 -fi - -curl -X PUT -T "${FILE}" \ - -H "Host: storage.googleapis.com" \ - -H "Date: ${dateValue}" \ - -H "Content-Type: ${contentType}" \ - -H "Authorization: AWS ${GCLOUD_KEY}:${signature}" \ - $fullUrl - -echo "upload done" - -curl -X POST --fail -s \ - -H "Content-Type: application/json" \ - -d "{\"content\": \"Build succeeded ✅\nBranch: $BRANCH\nPlatform: $PLATFORM-$ARCH\nLast commit: $(git log -1 --pretty=format:'%s by %an')\nhttps://build.livepeer.live/${BUCKET_PATH}\nSHA256:\n${FILE_SHA256}\"}" \ - $DISCORD_URL -echo "done" From 2c57642f7ca913db2a7e9ee9f69a5d63ba7516da Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 15 Aug 2024 16:39:49 +0200 Subject: [PATCH 169/203] ci: prevent build residual files This commit ensures that the `lp-builds` and `releases` folders are correctly removed so that they can not cause permissions problems when subsequent actions are run (see https://github.com/livepeer/go-livepeer/actions/runs/10404768099/job/28813960307). --- .github/workflows/build.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index d5223daa1..beb10f2a4 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -241,6 +241,11 @@ jobs: name: release-artifacts-${{ matrix.target.GOOS }}-${{ matrix.target.GOARCH }} path: releases/ + # Clean up the created files to ensure proper permissions and no residue + - name: Clean up + run: | + sudo rm -rf lp-builds releases + upload: name: Upload artifacts to google bucket permissions: From 829426973899645c9e238e20627117eb2356d385 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 15 Aug 2024 16:55:05 +0200 Subject: [PATCH 170/203] ci: reset permissions in build action This commit resets the permissions in each job of the build action to prevent permission errors. --- .github/workflows/build.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index beb10f2a4..0c2300adb 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -167,6 +167,9 @@ jobs: # for ref value discussion ref: ${{ github.event.pull_request.head.sha }} + - name: Reset workspace permissions + run: chown -R $USER:$USER $GITHUB_WORKSPACE + - name: Set up go id: go uses: actions/setup-go@v5 @@ -244,7 +247,7 @@ jobs: # Clean up the created files to ensure proper permissions and no residue - name: Clean up run: | - sudo rm -rf lp-builds releases + rm -rf lp-builds releases upload: name: Upload artifacts to google bucket From 85e09f3f226228c5987defc7774210e90257cd9b Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Fri, 16 Aug 2024 11:25:33 +0200 Subject: [PATCH 171/203] ci: temporarily run upload job as root This commit modifies the upload job in the `build.yaml` GitHub Action to temporarily run as root. This change addresses permission issues caused by a bug in GitHub's runner selection process (see https://github.com/actions/checkout/issues/1014). --- .github/workflows/build.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 0c2300adb..b52ba6eb2 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -167,9 +167,6 @@ jobs: # for ref value discussion ref: ${{ github.event.pull_request.head.sha }} - - name: Reset workspace permissions - run: chown -R $USER:$USER $GITHUB_WORKSPACE - - name: Set up go id: go uses: actions/setup-go@v5 @@ -255,6 +252,8 @@ jobs: contents: "read" id-token: "write" runs-on: ubuntu-latest + container: + options: --user root needs: - macos-build - linux-build From e08f0785c6a762c6712755a630af2785e36b6f30 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Fri, 16 Aug 2024 11:31:04 +0200 Subject: [PATCH 172/203] ci: fix broken binary upload action This commit fixes the broken binary upload action. --- .github/workflows/build.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index b52ba6eb2..8ca1fb9df 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -251,8 +251,9 @@ jobs: permissions: contents: "read" id-token: "write" - runs-on: ubuntu-latest + # runs-on: ubuntu-latest # TODO: re-enable when https://github.com/actions/checkout/issues/956 is solved. container: + image: ubuntu:latest options: --user root needs: - macos-build From 815b9817dd6e80b259dce2f06ec6cbf8035f10b0 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Fri, 16 Aug 2024 12:26:51 +0200 Subject: [PATCH 173/203] chore: revert build ci fixes This commit reverts the build ci fixes that were done in the previous commits since https://github.com/livepeer/go-livepeer/pull/3137 provides a better workarround. --- .github/workflows/build.yaml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 8ca1fb9df..d5223daa1 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -241,20 +241,12 @@ jobs: name: release-artifacts-${{ matrix.target.GOOS }}-${{ matrix.target.GOARCH }} path: releases/ - # Clean up the created files to ensure proper permissions and no residue - - name: Clean up - run: | - rm -rf lp-builds releases - upload: name: Upload artifacts to google bucket permissions: contents: "read" id-token: "write" - # runs-on: ubuntu-latest # TODO: re-enable when https://github.com/actions/checkout/issues/956 is solved. - container: - image: ubuntu:latest - options: --user root + runs-on: ubuntu-latest needs: - macos-build - linux-build From bd0c70a84b6803090f06ee8cbd17e0fb0afd78d4 Mon Sep 17 00:00:00 2001 From: hjpotter92 Date: Fri, 16 Aug 2024 15:09:03 +0530 Subject: [PATCH 174/203] workflows: Revert to ubuntu-20 to stop selecting self-hosted runners --- .github/workflows/build.yaml | 21 +++++++++++++++------ .github/workflows/git.yaml | 2 +- .github/workflows/test.yaml | 4 ++-- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index d5223daa1..baf30ba59 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -246,7 +246,7 @@ jobs: permissions: contents: "read" id-token: "write" - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 needs: - macos-build - linux-build @@ -259,6 +259,15 @@ jobs: # for ref value discussion ref: ${{ github.event.pull_request.head.sha }} + - name: Set up python + id: python + uses: actions/setup-go@v5 + with: + python-version: 3.12 + cache: pip + cache-dependency-path: .github/requirements.txt + update-environment: true + - name: Download artifacts uses: actions/download-artifact@v4 with: @@ -312,12 +321,12 @@ jobs: env: GITHUB_CONTEXT_JSON: ${{ toJson(github) }} run: | - pip install -U discord-webhook --no-cache-dir + pip install -r .github/requirements.txt python .github/discord-embed-webhook.py \ - --ref-name="${{ (github.ref_type == 'tag' && github.ref_name) || (github.event.pull_request.head.sha || github.sha) }}" \ - --discord-url="${{ secrets.DISCORD_URL }}" \ - --git-commit="$(git log -1 --pretty=format:'%s')" \ - --git-committer="$(git log -1 --pretty=format:'%an')" + --ref-name="${{ (github.ref_type == 'tag' && github.ref_name) || (github.event.pull_request.head.sha || github.sha) }}" \ + --discord-url="${{ secrets.DISCORD_URL }}" \ + --git-commit="$(git log -1 --pretty=format:'%s')" \ + --git-committer="$(git log -1 --pretty=format:'%an')" - name: Notify new build upload run: curl -X POST https://holy-bread-207a.livepeer.workers.dev diff --git a/.github/workflows/git.yaml b/.github/workflows/git.yaml index dad9e0925..4ed7f0e43 100644 --- a/.github/workflows/git.yaml +++ b/.github/workflows/git.yaml @@ -9,7 +9,7 @@ concurrency: jobs: block-fixup: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - name: Check out code uses: actions/checkout@v4.1.7 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index fbbfb6752..2e5636f85 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -124,7 +124,7 @@ jobs: codeql: name: Perform CodeQL analysis - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - name: Check out code @@ -148,7 +148,7 @@ jobs: editorconfig: name: Run editorconfig checker - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - name: Check out code uses: actions/checkout@v4.1.7 From 200e145a19a919857614d52607d07067e2d43bd9 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Fri, 16 Aug 2024 16:32:50 +0200 Subject: [PATCH 175/203] ci: add protoc build dependencies (#3139) This commit adds the protocol-buffer dependencies needed to build on Ubuntu 20.04. --- .github/workflows/build.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index d3e9951fc..329b825f7 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -101,7 +101,8 @@ jobs: && apt update \ && apt -yqq install \ nasm clang-14 clang-tools-14 lld-14 build-essential pkg-config autoconf git python3 \ - gcc-mingw-w64 libgcc-9-dev-arm64-cross mingw-w64-tools gcc-mingw-w64-x86-64 + gcc-mingw-w64 libgcc-9-dev-arm64-cross mingw-w64-tools gcc-mingw-w64-x86-64 \ + golang-goprotobuf-dev protobuf-compiler-grpc update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-14 30 \ && update-alternatives --install /usr/bin/clang clang /usr/bin/clang-14 30 \ From 0ae26d3fc6488bf09b985cfd68b4b5ad32f90bd6 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Fri, 16 Aug 2024 19:24:03 +0200 Subject: [PATCH 176/203] ci(ai): add temporary ai-video latest binary url upload (#3142) This commit ensures that the `build.yaml` action script also creates a release under a `ai-video/latest` path. This was done to simplify the binary installation in the docs. --- .github/workflows/build.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 329b825f7..e7d0bdc21 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -317,6 +317,17 @@ jobs: parent: false process_gcloudignore: false + # Create temporary latest url for the 'ai-video' branch. + # TODO: Once 'ai-video' is merged into the main branch, this section should be removed. + - name: Upload release archives to latest url + if: startsWith(github.ref, 'refs/tags/') && github.ref_name == 'ai-video' && env.current_tag == env.highest_tag + uses: google-github-actions/upload-cloud-storage@v2 + with: + path: "releases" + destination: "build.livepeer.live/go-livepeer/ai-video/latest" + parent: false + process_gcloudignore: false + - name: Trigger discord webhook shell: bash env: From c229c2a38984f3df534f11532bdc7f5e26f4ae5c Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Fri, 16 Aug 2024 20:25:03 +0200 Subject: [PATCH 177/203] fix(ai): handle missing pricePerUnit in AIConfig (#3141) This commit ensures that the code correctly handles the case where no pricePerUnit is set in the AIConfig. It also adds logic to use the value provided by the `pricePerUnit` flag if no price is given and the flag exists. --- cmd/livepeer/starter/starter.go | 39 +++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index 3db6c2aeb..33cd9066e 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -1118,7 +1118,7 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { return } - // Get base pixels per unit. + // Get base pixels and price per unit. pixelsPerUnitBase, ok := new(big.Rat).SetString(*cfg.PixelsPerUnit) if !ok || !pixelsPerUnitBase.IsInt() { panic(fmt.Errorf("-pixelsPerUnit must be a valid integer, provided %v", *cfg.PixelsPerUnit)) @@ -1127,6 +1127,16 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { // Can't divide by 0 panic(fmt.Errorf("-pixelsPerUnit must be > 0, provided %v", *cfg.PixelsPerUnit)) } + pricePerUnitBase := new(big.Rat) + currencyBase := "" + if cfg.PricePerUnit != nil { + pricePerUnit, currency, err := parsePricePerUnit(*cfg.PricePerUnit) + if err != nil || pricePerUnit.Sign() < 0 { + panic(fmt.Errorf("-pricePerUnit must be a valid positive integer with an optional currency, provided %v", *cfg.PricePerUnit)) + } + pricePerUnitBase = pricePerUnit + currencyBase = currency + } if *cfg.AIModels != "" { configs, err := core.ParseAIModelConfigs(*cfg.AIModels) @@ -1143,20 +1153,26 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { pixelsPerUnit := config.PixelsPerUnit.Rat if config.PixelsPerUnit.Rat == nil { pixelsPerUnit = pixelsPerUnitBase - } else { - if !pixelsPerUnit.IsInt() || pixelsPerUnit.Sign() <= 0 { - panic(fmt.Errorf("'pixelsPerUnit' value specified for model '%v' in pipeline '%v' must be a valid positive integer, provided %v", config.ModelID, config.Pipeline, config.PixelsPerUnit)) - } + } else if !pixelsPerUnit.IsInt() || pixelsPerUnit.Sign() <= 0 { + panic(fmt.Errorf("'pixelsPerUnit' value specified for model '%v' in pipeline '%v' must be a valid positive integer, provided %v", config.ModelID, config.Pipeline, config.PixelsPerUnit)) } + pricePerUnit := config.PricePerUnit.Rat - if err != nil { - panic(fmt.Errorf("'pricePerUnit' value specified for model '%v' in pipeline '%v' must be a valid integer with an optional currency, provided %v", config.ModelID, config.Pipeline, config.PricePerUnit)) - } else if pricePerUnit.Sign() < 0 { - panic(fmt.Errorf("'pricePerUnit' value specified for model '%v' in pipeline '%v' must be >= 0, provided %v", config.ModelID, config.Pipeline, config.PricePerUnit)) + currency := config.Currency + if pricePerUnit == nil { + if pricePerUnitBase.Sign() == 0 { + panic(fmt.Errorf("'pricePerUnit' must be set for model '%v' in pipeline '%v'", config.ModelID, config.Pipeline)) + } + pricePerUnit = pricePerUnitBase + currency = currencyBase + glog.Warningf("No 'pricePerUnit' specified for model '%v' in pipeline '%v'. Using default value from `-pricePerUnit`: %v", config.ModelID, config.Pipeline, *cfg.PricePerUnit) + } else if !pricePerUnit.IsInt() || pricePerUnit.Sign() <= 0 { + panic(fmt.Errorf("'pricePerUnit' value specified for model '%v' in pipeline '%v' must be a valid positive integer, provided %v", config.ModelID, config.Pipeline, config.PricePerUnit)) } + pricePerPixel := new(big.Rat).Quo(pricePerUnit, pixelsPerUnit) - autoPrice, err = core.NewAutoConvertedPrice(config.Currency, pricePerPixel, nil) + autoPrice, err = core.NewAutoConvertedPrice(currency, pricePerPixel, nil) if err != nil { panic(fmt.Errorf("error converting price: %v", err)) } @@ -1254,8 +1270,7 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { capability := aiCaps[len(aiCaps)-1] price := n.GetBasePriceForCap("default", capability, config.ModelID) if *cfg.Network != "offchain" { - pricePerUnit := price.Num().Int64() / price.Denom().Int64() - glog.V(6).Infof("Capability %s (ID: %v) advertised with model constraint %s at price %d wei per compute unit", config.Pipeline, capability, config.ModelID, pricePerUnit) + glog.V(6).Infof("Capability %s (ID: %v) advertised with model constraint %s at price %s wei per compute unit", config.Pipeline, capability, config.ModelID, price.FloatString(3)) } else { glog.V(6).Infof("Capability %s (ID: %v) advertised with model constraint %s", config.Pipeline, capability, config.ModelID) } From 09a9ab0c29ac440e4a1e09e968fb4754e8d482c8 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Mon, 19 Aug 2024 15:59:11 +0200 Subject: [PATCH 178/203] chore(ai): update AI release version This commit updates the AI release version to the experimental alpha version so that the build docker image has the right versioning. --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 4d01880a7..56225bcbb 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.7.6 \ No newline at end of file +0.7.6-ai-alpha From 60e082ed182a26dbe609f24de87bbf44140dc5f8 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Mon, 19 Aug 2024 17:37:21 +0200 Subject: [PATCH 179/203] ci(ai): ensure latest tag ai release (#3143) * ci(ai): ensure latest tag binary release This commit ensure that for the AI branch a latest binry is created to improve the install workflow in the docs. --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index e7d0bdc21..d0a018f4c 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -320,7 +320,7 @@ jobs: # Create temporary latest url for the 'ai-video' branch. # TODO: Once 'ai-video' is merged into the main branch, this section should be removed. - name: Upload release archives to latest url - if: startsWith(github.ref, 'refs/tags/') && github.ref_name == 'ai-video' && env.current_tag == env.highest_tag + if: startsWith(github.ref, 'refs/tags/') && contains(github.ref, '-ai.') uses: google-github-actions/upload-cloud-storage@v2 with: path: "releases" From d55426b54437d59bae7661e776cad7c6db8f1d82 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Mon, 19 Aug 2024 17:37:54 +0200 Subject: [PATCH 180/203] chore(ai): release v0.7.6-ai.1 This commit releases the new rebased `ai-video` branch so that it can be deployed by orchestrators and Gateways (see https://github.com/livepeer/go-livepeer/pull/3103). --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 56225bcbb..4fe2aca20 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.7.6-ai-alpha +0.7.6-ai.1 From 0fe1c210f67821e1ffb853e2c9f9ed743041bf2a Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Tue, 20 Aug 2024 10:41:26 +0200 Subject: [PATCH 181/203] ci(ai): improve latest release binary ci logic This commit ensures that the binary release is pushed to a latest folder in the google bucket when it was triggered by the latest ai-video release. --- .github/workflows/build.yaml | 22 +++++++++++++++++----- ci_env.sh | 5 +++++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index d0a018f4c..ba1bacb76 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -17,6 +17,8 @@ jobs: linux-build: name: Build binaries for ${{ matrix.target.GOOS }}-${{ matrix.target.type }}-${{ matrix.target.GOARCH }} runs-on: ubuntu-20.04 + outputs: + latest_tag: ${{ steps.build-binaries.outputs.latest_tag }} container: image: ${{ matrix.target.container }} env: @@ -116,6 +118,7 @@ jobs: run: ./install_ffmpeg.sh - name: Build binaries + id: build-binaries env: GHA_REF: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || github.ref }} run: | @@ -317,14 +320,23 @@ jobs: parent: false process_gcloudignore: false - # Create temporary latest url for the 'ai-video' branch. - # TODO: Once 'ai-video' is merged into the main branch, this section should be removed. - - name: Upload release archives to latest url - if: startsWith(github.ref, 'refs/tags/') && contains(github.ref, '-ai.') + # Update the latest release + - name: Upload release archives to Google Cloud latest folder + id: upload-archives-latest + if: ${{ github.ref == format('refs/tags/{0}', needs.linux-build.latest_tag) }} uses: google-github-actions/upload-cloud-storage@v2 with: path: "releases" - destination: "build.livepeer.live/go-livepeer/ai-video/latest" + destination: "build.livepeer.live/${{ github.event.repository.name }}/ai-video/latest" + + # Update the latest branch manifest + - name: Upload branch manifest file to Google Cloud latest folder + id: upload-manifest-latest + if: ${{ github.ref == format('refs/tags/{0}', needs.linux-build.latest_tag) }} + uses: google-github-actions/upload-cloud-storage@v2 + with: + path: ${{ steps.branch-manifest.outputs.manifest-file }} + destination: "build.livepeer.live/${{ github.event.repository.name }}/ai-video/latest" parent: false process_gcloudignore: false diff --git a/ci_env.sh b/ci_env.sh index f05ac2ac5..c32409838 100755 --- a/ci_env.sh +++ b/ci_env.sh @@ -59,4 +59,9 @@ if [[ "$CI" == "true" ]]; then echo "build-tags=${BUILD_TAGS}" >>"$GITHUB_OUTPUT" fi +# Retrieve latest release tag +git fetch --tags +latest_tag=$(git tag -l "v*" | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+-ai.[0-9]+$' | sort -V | tail -n 1) +echo "latest_tag=$latest_tag" >>"$GITHUB_OUTPUT" + exec "$@" From d162ce7c87be1d2ba75f7afd558d8ddf9c3ea175 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Tue, 20 Aug 2024 10:43:22 +0200 Subject: [PATCH 182/203] ci(ai): temporary simplify build action for tests This commit disables parts of the build actions and changes the trigger to see if the new logic works. --- .github/workflows/build.yaml | 207 ++++++++++++++++++----------------- 1 file changed, 104 insertions(+), 103 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index ba1bacb76..05541d6a6 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -5,7 +5,8 @@ on: push: branches: # - master - - ai-video + # - ai-video + - improve_latest_binary_release_ci_logic tags: - "v*" @@ -27,10 +28,10 @@ jobs: fail-fast: false matrix: target: - - GOOS: linux - GOARCH: amd64 - container: ubuntu:20.04 - type: cpu + # - GOOS: linux + # GOARCH: amd64 + # container: ubuntu:20.04 + # type: cpu # - GOOS: linux # GOARCH: arm64 @@ -147,103 +148,103 @@ jobs: name: release-artifacts-${{ matrix.target.GOOS }}-${{ matrix.target.type }}-${{ matrix.target.GOARCH }} path: releases/ - macos-build: - name: Build binaries for ${{ matrix.target.GOOS }}-${{ matrix.target.GOARCH }} - runs-on: ${{ matrix.target.runner }} - strategy: - fail-fast: false - matrix: - target: - - GOOS: darwin - GOARCH: amd64 - runner: macos-14-large - - - GOOS: darwin - GOARCH: arm64 - runner: macos-14-xlarge - - steps: - - name: Check out code - uses: actions/checkout@v4.1.7 - with: - fetch-depth: 0 - # Check https://github.com/livepeer/go-livepeer/pull/1891 - # for ref value discussion - ref: ${{ github.event.pull_request.head.sha }} - - - name: Set up go - id: go - uses: actions/setup-go@v5 - with: - go-version: 1.21.5 - cache: true - cache-dependency-path: go.sum - - - name: Set build environment - run: | - echo "GOARCH=${{ matrix.target.GOARCH }}" >> $GITHUB_ENV - echo "GOOS=${{ matrix.target.GOOS }}" >> $GITHUB_ENV - echo "GO_BUILD_DIR=lp-builds/" >> $GITHUB_ENV - echo "LP_BUILD_DIR=livepeer-${{ matrix.target.GOOS }}-${{ matrix.target.GOARCH }}" >> $GITHUB_ENV - mkdir -p lp-builds/ releases/ - - - name: Cache ffmpeg - id: cache-ffmpeg - uses: actions/cache@v4 - with: - path: ~/compiled - key: ${{ runner.os }}-${{ matrix.target.GOOS }}-${{ matrix.target.GOARCH }}-ffmpeg-${{ hashFiles('**/install_ffmpeg.sh') }} - - - name: Install dependencies - run: brew install coreutils pkg-config - - - name: Install go modules - # if: steps.go.outputs.cache-hit != 'true' - run: go mod download - - - name: Install ffmpeg - if: steps.cache-ffmpeg.outputs.cache-hit != 'true' - run: ./install_ffmpeg.sh - - - name: Build binaries - env: - GHA_REF: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || github.ref }} - run: | - git config --global --add safe.directory '*' - export PKG_CONFIG_PATH=~/compiled/lib/pkgconfig - ./ci_env.sh make - - - uses: actions-ecosystem/action-regex-match@v2 - id: match-tag - with: - text: ${{ github.ref_name }} - regex: '^(master|main|ai-video|v[0-9]+\.\d+\.\d+)$' - - - name: Codesign and notarize binaries - if: steps.match-tag.outputs.match != '' && matrix.target.GOOS == 'darwin' - uses: livepeer/action-gh-codesign-apple@latest - with: - developer-certificate-id: ${{ secrets.CI_MACOS_CERTIFICATE_ID }} - developer-certificate-base64: ${{ secrets.CI_MACOS_CERTIFICATE_BASE64 }} - developer-certificate-password: ${{ secrets.CI_MACOS_CERTIFICATE_PASSWORD }} - app-notarization-email: ${{ secrets.CI_MACOS_NOTARIZATION_USER }} - app-notarization-password: ${{ secrets.CI_MACOS_NOTARIZATION_PASSWORD }} - app-notarization-team-id: ${{ secrets.CI_MACOS_NOTARIZATION_TEAM_ID }} - binary-path: "lp-builds/" - - - name: Archive binaries - if: matrix.platform.name != 'windows' - run: | - mkdir -p "${GO_BUILD_DIR}/${LP_BUILD_DIR}/" - cd "$GO_BUILD_DIR/" - find . -type f -exec mv '{}' "$LP_BUILD_DIR" \; - tar -czvf "../releases/${LP_BUILD_DIR}.tar.gz" . - - - name: Upload artifacts for cutting release - uses: actions/upload-artifact@v4 - with: - name: release-artifacts-${{ matrix.target.GOOS }}-${{ matrix.target.GOARCH }} - path: releases/ + # macos-build: + # name: Build binaries for ${{ matrix.target.GOOS }}-${{ matrix.target.GOARCH }} + # runs-on: ${{ matrix.target.runner }} + # strategy: + # fail-fast: false + # matrix: + # target: + # - GOOS: darwin + # GOARCH: amd64 + # runner: macos-14-large + + # - GOOS: darwin + # GOARCH: arm64 + # runner: macos-14-xlarge + + # steps: + # - name: Check out code + # uses: actions/checkout@v4.1.7 + # with: + # fetch-depth: 0 + # # Check https://github.com/livepeer/go-livepeer/pull/1891 + # # for ref value discussion + # ref: ${{ github.event.pull_request.head.sha }} + + # - name: Set up go + # id: go + # uses: actions/setup-go@v5 + # with: + # go-version: 1.21.5 + # cache: true + # cache-dependency-path: go.sum + + # - name: Set build environment + # run: | + # echo "GOARCH=${{ matrix.target.GOARCH }}" >> $GITHUB_ENV + # echo "GOOS=${{ matrix.target.GOOS }}" >> $GITHUB_ENV + # echo "GO_BUILD_DIR=lp-builds/" >> $GITHUB_ENV + # echo "LP_BUILD_DIR=livepeer-${{ matrix.target.GOOS }}-${{ matrix.target.GOARCH }}" >> $GITHUB_ENV + # mkdir -p lp-builds/ releases/ + + # - name: Cache ffmpeg + # id: cache-ffmpeg + # uses: actions/cache@v4 + # with: + # path: ~/compiled + # key: ${{ runner.os }}-${{ matrix.target.GOOS }}-${{ matrix.target.GOARCH }}-ffmpeg-${{ hashFiles('**/install_ffmpeg.sh') }} + + # - name: Install dependencies + # run: brew install coreutils pkg-config + + # - name: Install go modules + # # if: steps.go.outputs.cache-hit != 'true' + # run: go mod download + + # - name: Install ffmpeg + # if: steps.cache-ffmpeg.outputs.cache-hit != 'true' + # run: ./install_ffmpeg.sh + + # - name: Build binaries + # env: + # GHA_REF: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || github.ref }} + # run: | + # git config --global --add safe.directory '*' + # export PKG_CONFIG_PATH=~/compiled/lib/pkgconfig + # ./ci_env.sh make + + # - uses: actions-ecosystem/action-regex-match@v2 + # id: match-tag + # with: + # text: ${{ github.ref_name }} + # regex: '^(master|main|ai-video|v[0-9]+\.\d+\.\d+)$' + + # - name: Codesign and notarize binaries + # if: steps.match-tag.outputs.match != '' && matrix.target.GOOS == 'darwin' + # uses: livepeer/action-gh-codesign-apple@latest + # with: + # developer-certificate-id: ${{ secrets.CI_MACOS_CERTIFICATE_ID }} + # developer-certificate-base64: ${{ secrets.CI_MACOS_CERTIFICATE_BASE64 }} + # developer-certificate-password: ${{ secrets.CI_MACOS_CERTIFICATE_PASSWORD }} + # app-notarization-email: ${{ secrets.CI_MACOS_NOTARIZATION_USER }} + # app-notarization-password: ${{ secrets.CI_MACOS_NOTARIZATION_PASSWORD }} + # app-notarization-team-id: ${{ secrets.CI_MACOS_NOTARIZATION_TEAM_ID }} + # binary-path: "lp-builds/" + + # - name: Archive binaries + # if: matrix.platform.name != 'windows' + # run: | + # mkdir -p "${GO_BUILD_DIR}/${LP_BUILD_DIR}/" + # cd "$GO_BUILD_DIR/" + # find . -type f -exec mv '{}' "$LP_BUILD_DIR" \; + # tar -czvf "../releases/${LP_BUILD_DIR}.tar.gz" . + + # - name: Upload artifacts for cutting release + # uses: actions/upload-artifact@v4 + # with: + # name: release-artifacts-${{ matrix.target.GOOS }}-${{ matrix.target.GOARCH }} + # path: releases/ upload: name: Upload artifacts to google bucket @@ -252,7 +253,7 @@ jobs: id-token: "write" runs-on: ubuntu-20.04 needs: - - macos-build + # - macos-build - linux-build steps: - name: Check out code From 9ec1e053330c58e6f6a5ad1ff39c4c1a65527775 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Tue, 20 Aug 2024 11:02:55 +0200 Subject: [PATCH 183/203] ci: add temp condition print --- .github/workflows/build.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 05541d6a6..b8d11660d 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -321,6 +321,13 @@ jobs: parent: false process_gcloudignore: false + # Print condition + - name: Print latest condition + run: | + echo "Ref: ${{ github.ref }}" + echo "Latest tag: ${{ needs.linux-build.outputs.latest_tag }}" + echo "Is latest tag: ${{ github.ref == format('refs/tags/{0}', needs.linux-build.outputs.latest_tag) }}" + # Update the latest release - name: Upload release archives to Google Cloud latest folder id: upload-archives-latest From 42b7c854df08b025daad1266123509baf92a0607 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Tue, 20 Aug 2024 13:15:31 +0200 Subject: [PATCH 184/203] ci(ai): add latest tag check into build publish job This commit ensure the latest tag is checked in the publish job of the build action. --- .github/workflows/build.yaml | 16 ++++++++++++---- ci_env.sh | 5 ----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index b8d11660d..b33bfc3ea 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -328,23 +328,31 @@ jobs: echo "Latest tag: ${{ needs.linux-build.outputs.latest_tag }}" echo "Is latest tag: ${{ github.ref == format('refs/tags/{0}', needs.linux-build.outputs.latest_tag) }}" + # Get the latest tag + - name: Get latest tag + id: get-latest-tag + run: | + git fetch --tags + latest_tag=$(git tag -l "v*" | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+-ai.[0-9]+$' | sort -V | tail -n 1) + echo "::set-output name=latest_tag::$latest_tag" + # Update the latest release - name: Upload release archives to Google Cloud latest folder id: upload-archives-latest - if: ${{ github.ref == format('refs/tags/{0}', needs.linux-build.latest_tag) }} + if: ${{ github.ref == format('refs/tags/{0}', steps.get-latest-tag.outputs.latest_tag) }} uses: google-github-actions/upload-cloud-storage@v2 with: path: "releases" - destination: "build.livepeer.live/${{ github.event.repository.name }}/ai-video/latest" + destination: "build.livepeer.live/${{ github.event.repository.name }}/ai-video/stable" # Update the latest branch manifest - name: Upload branch manifest file to Google Cloud latest folder id: upload-manifest-latest - if: ${{ github.ref == format('refs/tags/{0}', needs.linux-build.latest_tag) }} + if: ${{ github.ref == format('refs/tags/{0}', steps.get-latest-tag.outputs.latest_tag) }} uses: google-github-actions/upload-cloud-storage@v2 with: path: ${{ steps.branch-manifest.outputs.manifest-file }} - destination: "build.livepeer.live/${{ github.event.repository.name }}/ai-video/latest" + destination: "build.livepeer.live/${{ github.event.repository.name }}/ai-video/stable" parent: false process_gcloudignore: false diff --git a/ci_env.sh b/ci_env.sh index c32409838..f05ac2ac5 100755 --- a/ci_env.sh +++ b/ci_env.sh @@ -59,9 +59,4 @@ if [[ "$CI" == "true" ]]; then echo "build-tags=${BUILD_TAGS}" >>"$GITHUB_OUTPUT" fi -# Retrieve latest release tag -git fetch --tags -latest_tag=$(git tag -l "v*" | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+-ai.[0-9]+$' | sort -V | tail -n 1) -echo "latest_tag=$latest_tag" >>"$GITHUB_OUTPUT" - exec "$@" From c6f42a226fd2fdf3f8a4674e3ed2864d231a435f Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Tue, 20 Aug 2024 13:16:35 +0200 Subject: [PATCH 185/203] fixup! ci(ai): add latest tag check into build publish job --- .github/workflows/build.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index b33bfc3ea..588baf6e8 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -119,7 +119,6 @@ jobs: run: ./install_ffmpeg.sh - name: Build binaries - id: build-binaries env: GHA_REF: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || github.ref }} run: | From ea75b754ed7b05a58e152e76e27b71e4807db884 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Tue, 20 Aug 2024 13:37:35 +0200 Subject: [PATCH 186/203] fixup! fixup! ci(ai): add latest tag check into build publish job --- .github/workflows/build.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 588baf6e8..399b1d044 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -333,7 +333,9 @@ jobs: run: | git fetch --tags latest_tag=$(git tag -l "v*" | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+-ai.[0-9]+$' | sort -V | tail -n 1) - echo "::set-output name=latest_tag::$latest_tag" + echo "latest_tag=$latest_tag" >> $GITHUB_OUTPUT + echo "Latest tag: $latest_tag" + echo "GitHub Ref: ${{ github.ref }}" # Update the latest release - name: Upload release archives to Google Cloud latest folder From a07c38e8fbb2e28eb86c8bc28785f8122628dea0 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Tue, 20 Aug 2024 14:25:56 +0200 Subject: [PATCH 187/203] fixup! fixup! fixup! ci(ai): add latest tag check into build publish job --- .github/workflows/build.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 399b1d044..eb1ddc1f5 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -338,16 +338,18 @@ jobs: echo "GitHub Ref: ${{ github.ref }}" # Update the latest release - - name: Upload release archives to Google Cloud latest folder + - name: Upload release archives to Google Cloud stable folder id: upload-archives-latest if: ${{ github.ref == format('refs/tags/{0}', steps.get-latest-tag.outputs.latest_tag) }} uses: google-github-actions/upload-cloud-storage@v2 with: path: "releases" destination: "build.livepeer.live/${{ github.event.repository.name }}/ai-video/stable" + parent: false + process_gcloudignore: false # Update the latest branch manifest - - name: Upload branch manifest file to Google Cloud latest folder + - name: Upload branch manifest file to Google Cloud stable folder id: upload-manifest-latest if: ${{ github.ref == format('refs/tags/{0}', steps.get-latest-tag.outputs.latest_tag) }} uses: google-github-actions/upload-cloud-storage@v2 From 9397d8099e3244147e88466a29093cfaa33eac58 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Tue, 20 Aug 2024 14:30:32 +0200 Subject: [PATCH 188/203] chore: update to version 0.7.7-ai.1 This commit ensures the ai network software is in sync with the main branch. --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 4fe2aca20..f1d6f0558 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.7.6-ai.1 +0.7.7-ai.1 From b51577e070e7d23b11c143e01871ec604a65bcad Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Tue, 20 Aug 2024 15:02:00 +0200 Subject: [PATCH 189/203] ci(ai): enable mac and linux builds This commit enables the macos and linux-amd64 builds. --- .github/workflows/build.yaml | 202 +++++++++++++++++------------------ 1 file changed, 101 insertions(+), 101 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index eb1ddc1f5..ea2e35b5b 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -28,10 +28,10 @@ jobs: fail-fast: false matrix: target: - # - GOOS: linux - # GOARCH: amd64 - # container: ubuntu:20.04 - # type: cpu + - GOOS: linux + GOARCH: amd64 + container: ubuntu:20.04 + type: cpu # - GOOS: linux # GOARCH: arm64 @@ -147,103 +147,103 @@ jobs: name: release-artifacts-${{ matrix.target.GOOS }}-${{ matrix.target.type }}-${{ matrix.target.GOARCH }} path: releases/ - # macos-build: - # name: Build binaries for ${{ matrix.target.GOOS }}-${{ matrix.target.GOARCH }} - # runs-on: ${{ matrix.target.runner }} - # strategy: - # fail-fast: false - # matrix: - # target: - # - GOOS: darwin - # GOARCH: amd64 - # runner: macos-14-large - - # - GOOS: darwin - # GOARCH: arm64 - # runner: macos-14-xlarge - - # steps: - # - name: Check out code - # uses: actions/checkout@v4.1.7 - # with: - # fetch-depth: 0 - # # Check https://github.com/livepeer/go-livepeer/pull/1891 - # # for ref value discussion - # ref: ${{ github.event.pull_request.head.sha }} - - # - name: Set up go - # id: go - # uses: actions/setup-go@v5 - # with: - # go-version: 1.21.5 - # cache: true - # cache-dependency-path: go.sum - - # - name: Set build environment - # run: | - # echo "GOARCH=${{ matrix.target.GOARCH }}" >> $GITHUB_ENV - # echo "GOOS=${{ matrix.target.GOOS }}" >> $GITHUB_ENV - # echo "GO_BUILD_DIR=lp-builds/" >> $GITHUB_ENV - # echo "LP_BUILD_DIR=livepeer-${{ matrix.target.GOOS }}-${{ matrix.target.GOARCH }}" >> $GITHUB_ENV - # mkdir -p lp-builds/ releases/ - - # - name: Cache ffmpeg - # id: cache-ffmpeg - # uses: actions/cache@v4 - # with: - # path: ~/compiled - # key: ${{ runner.os }}-${{ matrix.target.GOOS }}-${{ matrix.target.GOARCH }}-ffmpeg-${{ hashFiles('**/install_ffmpeg.sh') }} - - # - name: Install dependencies - # run: brew install coreutils pkg-config - - # - name: Install go modules - # # if: steps.go.outputs.cache-hit != 'true' - # run: go mod download - - # - name: Install ffmpeg - # if: steps.cache-ffmpeg.outputs.cache-hit != 'true' - # run: ./install_ffmpeg.sh - - # - name: Build binaries - # env: - # GHA_REF: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || github.ref }} - # run: | - # git config --global --add safe.directory '*' - # export PKG_CONFIG_PATH=~/compiled/lib/pkgconfig - # ./ci_env.sh make - - # - uses: actions-ecosystem/action-regex-match@v2 - # id: match-tag - # with: - # text: ${{ github.ref_name }} - # regex: '^(master|main|ai-video|v[0-9]+\.\d+\.\d+)$' - - # - name: Codesign and notarize binaries - # if: steps.match-tag.outputs.match != '' && matrix.target.GOOS == 'darwin' - # uses: livepeer/action-gh-codesign-apple@latest - # with: - # developer-certificate-id: ${{ secrets.CI_MACOS_CERTIFICATE_ID }} - # developer-certificate-base64: ${{ secrets.CI_MACOS_CERTIFICATE_BASE64 }} - # developer-certificate-password: ${{ secrets.CI_MACOS_CERTIFICATE_PASSWORD }} - # app-notarization-email: ${{ secrets.CI_MACOS_NOTARIZATION_USER }} - # app-notarization-password: ${{ secrets.CI_MACOS_NOTARIZATION_PASSWORD }} - # app-notarization-team-id: ${{ secrets.CI_MACOS_NOTARIZATION_TEAM_ID }} - # binary-path: "lp-builds/" - - # - name: Archive binaries - # if: matrix.platform.name != 'windows' - # run: | - # mkdir -p "${GO_BUILD_DIR}/${LP_BUILD_DIR}/" - # cd "$GO_BUILD_DIR/" - # find . -type f -exec mv '{}' "$LP_BUILD_DIR" \; - # tar -czvf "../releases/${LP_BUILD_DIR}.tar.gz" . - - # - name: Upload artifacts for cutting release - # uses: actions/upload-artifact@v4 - # with: - # name: release-artifacts-${{ matrix.target.GOOS }}-${{ matrix.target.GOARCH }} - # path: releases/ + macos-build: + name: Build binaries for ${{ matrix.target.GOOS }}-${{ matrix.target.GOARCH }} + runs-on: ${{ matrix.target.runner }} + strategy: + fail-fast: false + matrix: + target: + - GOOS: darwin + GOARCH: amd64 + runner: macos-14-large + + - GOOS: darwin + GOARCH: arm64 + runner: macos-14-xlarge + + steps: + - name: Check out code + uses: actions/checkout@v4.1.7 + with: + fetch-depth: 0 + # Check https://github.com/livepeer/go-livepeer/pull/1891 + # for ref value discussion + ref: ${{ github.event.pull_request.head.sha }} + + - name: Set up go + id: go + uses: actions/setup-go@v5 + with: + go-version: 1.21.5 + cache: true + cache-dependency-path: go.sum + + - name: Set build environment + run: | + echo "GOARCH=${{ matrix.target.GOARCH }}" >> $GITHUB_ENV + echo "GOOS=${{ matrix.target.GOOS }}" >> $GITHUB_ENV + echo "GO_BUILD_DIR=lp-builds/" >> $GITHUB_ENV + echo "LP_BUILD_DIR=livepeer-${{ matrix.target.GOOS }}-${{ matrix.target.GOARCH }}" >> $GITHUB_ENV + mkdir -p lp-builds/ releases/ + + - name: Cache ffmpeg + id: cache-ffmpeg + uses: actions/cache@v4 + with: + path: ~/compiled + key: ${{ runner.os }}-${{ matrix.target.GOOS }}-${{ matrix.target.GOARCH }}-ffmpeg-${{ hashFiles('**/install_ffmpeg.sh') }} + + - name: Install dependencies + run: brew install coreutils pkg-config + + - name: Install go modules + # if: steps.go.outputs.cache-hit != 'true' + run: go mod download + + - name: Install ffmpeg + if: steps.cache-ffmpeg.outputs.cache-hit != 'true' + run: ./install_ffmpeg.sh + + - name: Build binaries + env: + GHA_REF: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || github.ref }} + run: | + git config --global --add safe.directory '*' + export PKG_CONFIG_PATH=~/compiled/lib/pkgconfig + ./ci_env.sh make + + - uses: actions-ecosystem/action-regex-match@v2 + id: match-tag + with: + text: ${{ github.ref_name }} + regex: '^(master|main|ai-video|v[0-9]+\.\d+\.\d+)$' + + - name: Codesign and notarize binaries + if: steps.match-tag.outputs.match != '' && matrix.target.GOOS == 'darwin' + uses: livepeer/action-gh-codesign-apple@latest + with: + developer-certificate-id: ${{ secrets.CI_MACOS_CERTIFICATE_ID }} + developer-certificate-base64: ${{ secrets.CI_MACOS_CERTIFICATE_BASE64 }} + developer-certificate-password: ${{ secrets.CI_MACOS_CERTIFICATE_PASSWORD }} + app-notarization-email: ${{ secrets.CI_MACOS_NOTARIZATION_USER }} + app-notarization-password: ${{ secrets.CI_MACOS_NOTARIZATION_PASSWORD }} + app-notarization-team-id: ${{ secrets.CI_MACOS_NOTARIZATION_TEAM_ID }} + binary-path: "lp-builds/" + + - name: Archive binaries + if: matrix.platform.name != 'windows' + run: | + mkdir -p "${GO_BUILD_DIR}/${LP_BUILD_DIR}/" + cd "$GO_BUILD_DIR/" + find . -type f -exec mv '{}' "$LP_BUILD_DIR" \; + tar -czvf "../releases/${LP_BUILD_DIR}.tar.gz" . + + - name: Upload artifacts for cutting release + uses: actions/upload-artifact@v4 + with: + name: release-artifacts-${{ matrix.target.GOOS }}-${{ matrix.target.GOARCH }} + path: releases/ upload: name: Upload artifacts to google bucket From c05f23c49f76bd71170f6fadeb1b4b5de56957c1 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Tue, 20 Aug 2024 15:32:07 +0200 Subject: [PATCH 190/203] ci(ai): cleanup build action (#3147) This commit removes some redundant code in the bluild action. --- .github/workflows/build.yaml | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index ea2e35b5b..075c7c6ee 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -5,8 +5,7 @@ on: push: branches: # - master - # - ai-video - - improve_latest_binary_release_ci_logic + - ai-video tags: - "v*" @@ -18,8 +17,6 @@ jobs: linux-build: name: Build binaries for ${{ matrix.target.GOOS }}-${{ matrix.target.type }}-${{ matrix.target.GOARCH }} runs-on: ubuntu-20.04 - outputs: - latest_tag: ${{ steps.build-binaries.outputs.latest_tag }} container: image: ${{ matrix.target.container }} env: @@ -252,7 +249,7 @@ jobs: id-token: "write" runs-on: ubuntu-20.04 needs: - # - macos-build + - macos-build - linux-build steps: - name: Check out code @@ -320,13 +317,6 @@ jobs: parent: false process_gcloudignore: false - # Print condition - - name: Print latest condition - run: | - echo "Ref: ${{ github.ref }}" - echo "Latest tag: ${{ needs.linux-build.outputs.latest_tag }}" - echo "Is latest tag: ${{ github.ref == format('refs/tags/{0}', needs.linux-build.outputs.latest_tag) }}" - # Get the latest tag - name: Get latest tag id: get-latest-tag From 33eed89aa6a33e6f99a6347d72e586c2c2fda91c Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Tue, 20 Aug 2024 15:44:04 +0200 Subject: [PATCH 191/203] ci(ai): improve build ci doc comment This commit makes a small improvement to the doc comments in the build action. This was done to align the code with https://github.com/livepeer/go-livepeer/pull/3148/files. --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 075c7c6ee..e2bdc3fde 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -317,7 +317,7 @@ jobs: parent: false process_gcloudignore: false - # Get the latest tag + # Get the latest release tag - name: Get latest tag id: get-latest-tag run: | From f5c754e121eca758d673ec0c65d87197b4f45614 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Tue, 20 Aug 2024 22:06:30 +0200 Subject: [PATCH 192/203] chore(ai): release version v0.7.7-ai.3 This commit released the v0.7.7-ai.3 from the AI subnet software. --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index f1d6f0558..b50eecfd5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.7.7-ai.1 +0.7.7-ai.3 From 4f3947d1e8ec72becf4adc920b1c194bb0193078 Mon Sep 17 00:00:00 2001 From: ad-astra-video <99882368+ad-astra-video@users.noreply.github.com> Date: Fri, 23 Aug 2024 17:34:13 -0500 Subject: [PATCH 193/203] feat(pricing): expand gateway max price to set per capability/model id (#3116) This commit allows Gateways to specify the maximum pricing they are willing to pay for a given capability and model combination. Gateways use a single MaxPrice set at launch with the maxPricePerUnit flag, which serves as the default. Additionally, they can specify a JSON config using the maxPricePerCapability flag to set prices per capability and model ID. The maxPrice per capability can also be adjusted via the setBroadcastConfig endpoint of the CLI webserver. --- cmd/livepeer/livepeer.go | 1 + cmd/livepeer/starter/starter.go | 99 +++++++++++++++++++- cmd/livepeer/starter/starter_test.go | 28 ++++++ cmd/livepeer_cli/livepeer_cli.go | 1 + cmd/livepeer_cli/wizard_broadcast.go | 32 +++++++ common/util.go | 9 ++ core/ai.go | 20 ++++ core/ai_test.go | 20 ++++ core/orchestrator.go | 8 ++ monitor/census.go | 20 +++- server/ai_session.go | 26 +++++- server/broadcast.go | 88 +++++++++++++++-- server/handlers.go | 68 ++++++++++++++ server/handlers_test.go | 135 ++++++++++++++++++++++++++- server/mediaserver.go | 2 +- server/rpc_test.go | 48 +++++++++- server/selection.go | 7 +- server/selection_test.go | 8 +- server/webserver.go | 1 + 19 files changed, 597 insertions(+), 24 deletions(-) create mode 100644 core/ai_test.go diff --git a/cmd/livepeer/livepeer.go b/cmd/livepeer/livepeer.go index 1be4faff6..6e6bb382d 100755 --- a/cmd/livepeer/livepeer.go +++ b/cmd/livepeer/livepeer.go @@ -135,6 +135,7 @@ func parseLivepeerConfig() starter.LivepeerConfig { cfg.OrchPerfStatsURL = flag.String("orchPerfStatsUrl", *cfg.OrchPerfStatsURL, "URL of Orchestrator Performance Stream Tester") cfg.Region = flag.String("region", *cfg.Region, "Region in which a broadcaster is deployed; used to select the region while using the orchestrator's performance stats") cfg.MaxPricePerUnit = flag.String("maxPricePerUnit", *cfg.MaxPricePerUnit, "The maximum transcoding price per 'pixelsPerUnit' a broadcaster is willing to accept. If not set explicitly, broadcaster is willing to accept ANY price. Can be specified in wei or a custom currency in the format (e.g. 0.50USD). When using a custom currency, a corresponding price feed must be configured with -priceFeedAddr") + cfg.MaxPricePerCapability = flag.String("maxPricePerCapability", *cfg.MaxPricePerCapability, `json list of prices per capability/model or path to json config file. Use "model_id": "default" to price all models in a pipeline the same. Example: {"capabilities_prices": [{"pipeline": "text-to-image", "model_id": "stabilityai/sd-turbo", "price_per_unit": 1000, "pixels_per_unit": 1}, {"pipeline": "upscale", "model_id": "default", price_per_unit": 1200, "pixels_per_unit": 1}]}`) cfg.IgnoreMaxPriceIfNeeded = flag.Bool("ignoreMaxPriceIfNeeded", *cfg.IgnoreMaxPriceIfNeeded, "Set to true to allow exceeding max price condition if there is no O that meets this requirement") cfg.MinPerfScore = flag.Float64("minPerfScore", *cfg.MinPerfScore, "The minimum orchestrator's performance score a broadcaster is willing to accept") diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index ab1f956e8..5a8bb5bb2 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -105,6 +105,7 @@ type LivepeerConfig struct { OrchPerfStatsURL *string Region *string MaxPricePerUnit *string + MaxPricePerCapability *string IgnoreMaxPriceIfNeeded *bool MinPerfScore *float64 MaxSessions *string @@ -219,6 +220,7 @@ func DefaultLivepeerConfig() LivepeerConfig { defaultMaxTotalEV := "20000000000000" defaultDepositMultiplier := 1 defaultMaxPricePerUnit := "0" + defaultMaxPricePerCapability := "" defaultIgnoreMaxPriceIfNeeded := false defaultPixelsPerUnit := "1" defaultPriceFeedAddr := "0x639Fe6ab55C921f74e7fac1ee960C0B6293ba612" // ETH / USD price feed address on Arbitrum Mainnet @@ -316,6 +318,7 @@ func DefaultLivepeerConfig() LivepeerConfig { MaxTotalEV: &defaultMaxTotalEV, DepositMultiplier: &defaultDepositMultiplier, MaxPricePerUnit: &defaultMaxPricePerUnit, + MaxPricePerCapability: &defaultMaxPricePerCapability, IgnoreMaxPriceIfNeeded: &defaultIgnoreMaxPriceIfNeeded, PixelsPerUnit: &defaultPixelsPerUnit, PriceFeedAddr: &defaultPriceFeedAddr, @@ -962,6 +965,7 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { if err != nil { panic(fmt.Errorf("The maximum price per unit must be a valid integer with an optional currency, provided %v instead\n", *cfg.MaxPricePerUnit)) } + if maxPricePerUnit.Sign() > 0 { pricePerPixel := new(big.Rat).Quo(maxPricePerUnit, pixelsPerUnit) autoPrice, err := core.NewAutoConvertedPrice(currency, pricePerPixel, func(price *big.Rat) { @@ -978,6 +982,48 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { glog.Infof("Maximum transcoding price per pixel is not greater than 0: %v, broadcaster is currently set to accept ANY price.\n", *cfg.MaxPricePerUnit) glog.Infoln("To update the broadcaster's maximum acceptable transcoding price per pixel, use the CLI or restart the broadcaster with the appropriate 'maxPricePerUnit' and 'pixelsPerUnit' values") } + + if *cfg.MaxPricePerCapability != "" { + maxCapabilityPrices := getCapabilityPrices(*cfg.MaxPricePerCapability) + for _, p := range maxCapabilityPrices { + if p.PixelsPerUnit == nil { + p.PixelsPerUnit = pixelsPerUnit + } else if p.PixelsPerUnit.Sign() <= 0 { + glog.Infof("Pixels per unit for capability=%v model_id=%v in 'maxPricePerCapability' config is not greater than 0, using default pixelsPerUnit=%v.\n", p.Pipeline, p.ModelID, *cfg.PixelsPerUnit) + p.PixelsPerUnit = pixelsPerUnit + } + + if p.PricePerUnit == nil || p.PricePerUnit.Sign() <= 0 { + if maxPricePerUnit.Sign() > 0 { + glog.Infof("Maximum price per unit not set for capability=%v model_id=%v in 'maxPricePerCapability' config, using maxPricePerUnit=%v.\n", p.Pipeline, p.ModelID, *cfg.MaxPricePerUnit) + p.PricePerUnit = maxPricePerUnit + } else { + glog.Warningf("Maximum price per unit for capability=%v model_id=%v in 'maxPricePerCapability' config is not greater than 0, and 'maxPricePerUnit' not set, gateway is currently set to accept ANY price.\n", p.Pipeline, p.ModelID) + continue + } + } + + maxCapabilityPrice := new(big.Rat).Quo(p.PricePerUnit, p.PixelsPerUnit) + + cap, err := core.PipelineToCapability(p.Pipeline) + if err != nil { + panic(fmt.Errorf("Pipeline in 'maxPricePerCapability' config is not valid capability: %v\n", p.Pipeline)) + } + capName := core.CapabilityNameLookup[cap] + modelID := p.ModelID + autoCapPrice, err := core.NewAutoConvertedPrice(p.Currency, maxCapabilityPrice, func(price *big.Rat) { + if monitor.Enabled { + monitor.MaxPriceForCapability(capName, modelID, price) + } + glog.Infof("Maximum price per unit set to %v wei for capability=%v model_id=%v", price.FloatString(3), p.Pipeline, p.ModelID) + }) + if err != nil { + panic(fmt.Errorf("Error converting price: %v", err)) + } + + server.BroadcastCfg.SetCapabilityMaxPrice(cap, p.ModelID, autoCapPrice) + } + } } if n.NodeType == core.RedeemerNode { @@ -1781,8 +1827,8 @@ func getGatewayPrices(gatewayPrices string) []GatewayPrice { Currency string `json:"currency"` } `json:"broadcasters"` } - pricesFileContent, _ := common.ReadFromFile(gatewayPrices) + pricesFileContent, _ := common.ReadFromFile(gatewayPrices) err := json.Unmarshal([]byte(pricesFileContent), &pricesSet) if err != nil { glog.Errorf("gateway prices could not be parsed: %s", err) @@ -1810,6 +1856,57 @@ func getGatewayPrices(gatewayPrices string) []GatewayPrice { return prices } +type ModelPrice struct { + Pipeline string + ModelID string + PricePerUnit *big.Rat + PixelsPerUnit *big.Rat + Currency string +} + +func getCapabilityPrices(capabilitiesPrices string) []ModelPrice { + if capabilitiesPrices == "" { + return nil + } + + // Format of modelPrices json + // Model_id will be set to "default" to price all models in the pipeline if not specified. + // {"capabilities_prices": [ {"pipeline": "text-to-image", "model_id": "stabilityai/sd-turbo", "price_per_unit": 1000, "pixels_per_unit": 1}, {"pipeline": "image-to-video", "model_id": "default", "price_per_unit": 2000, "pixels_per_unit": 3} ] } + var pricesSet struct { + CapabilitiesPrices []struct { + Pipeline string `json:"pipeline"` + ModelID string `json:"model_id"` + PixelsPerUnit core.JSONRat `json:"pixels_per_unit"` + PricePerUnit core.JSONRat `json:"price_per_unit"` + Currency string `json:"currency"` + } `json:"capabilities_prices"` + } + + pricesFileContent, _ := common.ReadFromFile(capabilitiesPrices) + err := json.Unmarshal([]byte(pricesFileContent), &pricesSet) + if err != nil { + glog.Errorf("model prices could not be parsed: %s", err) + return nil + } + + prices := make([]ModelPrice, len(pricesSet.CapabilitiesPrices)) + for i, p := range pricesSet.CapabilitiesPrices { + if p.ModelID == "" { + p.ModelID = "default" + } + + prices[i] = ModelPrice{ + Pipeline: p.Pipeline, + ModelID: p.ModelID, + PricePerUnit: p.PricePerUnit.Rat, + PixelsPerUnit: p.PixelsPerUnit.Rat, + Currency: p.Currency, + } + } + + return prices +} + func createSelectionAlgorithm(cfg LivepeerConfig) (common.SelectionAlgorithm, error) { sumWeight := *cfg.SelectStakeWeight + *cfg.SelectPriceWeight + *cfg.SelectRandWeight if math.Abs(sumWeight-1.0) > 0.0001 { diff --git a/cmd/livepeer/starter/starter_test.go b/cmd/livepeer/starter/starter_test.go index 60df92789..9419b11a5 100644 --- a/cmd/livepeer/starter/starter_test.go +++ b/cmd/livepeer/starter/starter_test.go @@ -109,6 +109,34 @@ func TestParseGetGatewayPrices(t *testing.T) { } } +func TestMaxPricePerCapability(t *testing.T) { + assert := assert.New(t) + + jsonTemplate := `{"capabilities_prices": [ {"pipeline": "text-to-image", "model_id": "stabilityai/sd-turbo", "price_per_unit": 1000, "pixels_per_unit": 1}, {"pipeline": "image-to-video", "model_id": "default", "price_per_unit": 2000, "pixels_per_unit": 3}, {"pipeline": "image-to-image", "price_per_unit": 3000, "pixels_per_unit": 1} ] }` + + prices := getCapabilityPrices(jsonTemplate) + assert.NotNil(prices) + assert.Equal(3, len(prices)) + + // Confirm Pipeline and ModelID is parsed correctly + assert.Equal(prices[0].Pipeline, "text-to-image") + assert.Equal(prices[1].Pipeline, "image-to-video") + assert.Equal(prices[0].ModelID, "stabilityai/sd-turbo") + assert.Equal(prices[1].ModelID, "default") + + // Confirm prices are parsed correctly + price1 := new(big.Rat).Quo(prices[0].PricePerUnit, prices[0].PixelsPerUnit) + price2 := new(big.Rat).Quo(prices[1].PricePerUnit, prices[1].PixelsPerUnit) + assert.NotEqual(price1, price2) + assert.Equal(big.NewRat(1000, 1), price1) + assert.Equal(big.NewRat(2000, 3), price2) + + // Confirm modelID is "default" if not set and price set correctly + assert.Equal(prices[2].ModelID, "default") + price3 := new(big.Rat).Quo(prices[2].PricePerUnit, prices[2].PixelsPerUnit) + assert.Equal(big.NewRat(3000, 1), price3) +} + // Address provided to keystore file func TestParse_ParseEthKeystorePathValidFile(t *testing.T) { assert := assert.New(t) diff --git a/cmd/livepeer_cli/livepeer_cli.go b/cmd/livepeer_cli/livepeer_cli.go index a294bdab9..c6ea22fd1 100644 --- a/cmd/livepeer_cli/livepeer_cli.go +++ b/cmd/livepeer_cli/livepeer_cli.go @@ -102,6 +102,7 @@ func (w *wizard) initializeOptions() []wizardOpt { {desc: "Invoke \"cancel unlock of broadcasting funds\"", invoke: w.cancelUnlock, notOrchestrator: true}, {desc: "Invoke \"withdraw broadcasting funds\"", invoke: w.withdraw, notOrchestrator: true}, {desc: "Set broadcast config", invoke: w.setBroadcastConfig, notOrchestrator: true}, + {desc: "Set max price per capability", invoke: w.setBroadcastMaxPricePerCapability, notOrchestrator: true}, {desc: "Set maximum Ethereum gas price", invoke: w.setMaxGasPrice}, {desc: "Set minimum Ethereum gas price", invoke: w.setMinGasPrice}, {desc: "Get test LPT", invoke: w.requestTokens, testnet: true}, diff --git a/cmd/livepeer_cli/wizard_broadcast.go b/cmd/livepeer_cli/wizard_broadcast.go index 8f2c59e91..3dc3105b0 100644 --- a/cmd/livepeer_cli/wizard_broadcast.go +++ b/cmd/livepeer_cli/wizard_broadcast.go @@ -95,6 +95,38 @@ func (w *wizard) setBroadcastConfig() { } } +func (w *wizard) setBroadcastMaxPricePerCapability() { + fmt.Printf("Enter the pipeline to set price for - ") + pipeline := w.readString() + fmt.Printf("Enter the model id to set price for (default: default) - ") + modelID := w.readDefaultString("default") + fmt.Printf("Enter the maximum price to pay (default: 0) - ") + maxPricePerUnit := w.readDefaultString("0") + fmt.Printf("Enter the price currency (default: Wei) - ") + currency := w.readDefaultString("Wei") + pixelsPerUnit := "1" + + // Make default case insensitive. + if strings.EqualFold(modelID, "default") { + modelID = "default" + } + + val := url.Values{ + "maxPricePerUnit": {fmt.Sprintf("%v", maxPricePerUnit)}, + "pixelsPerUnit": {fmt.Sprintf("%v", pixelsPerUnit)}, + "currency": {fmt.Sprintf("%v", currency)}, + "pipeline": {fmt.Sprintf("%v", pipeline)}, + "modelID": {fmt.Sprintf("%v", modelID)}, + } + + resp, ok := httpPostWithParams(fmt.Sprintf("http://%v:%v/setMaxPriceForCapability", w.host, w.httpPort), val) + if !ok { + fmt.Printf("Error setting max price for capability: %v\n", resp) + } else { + fmt.Printf("Max price per capability set successfully\n") + } +} + func (w *wizard) idListToVideoProfileList(idList string, opts map[int]string) (string, error) { ids := strings.Split(idList, ",") diff --git a/common/util.go b/common/util.go index 8a9eb1521..6f30b57f4 100644 --- a/common/util.go +++ b/common/util.go @@ -359,6 +359,15 @@ func FixedToPrice(price int64) *big.Rat { return big.NewRat(price, priceScalingFactor) } +func PriceToInt64(price *big.Rat) (*big.Rat, error) { + fixed := new(big.Int).Div(price.Num(), price.Denom()) + if fixed.IsInt64() { + return big.NewRat(fixed.Int64(), int64(1)), nil + } else { + return nil, errors.New("price cannot convert to int64") + } +} + // BaseTokenAmountToFixed converts the base amount of a token (i.e. ETH/LPT) represented as a big.Int into a fixed point number represented // as a int64 using a scalingFactor of 100000 resulting in max decimal places of 5 func BaseTokenAmountToFixed(baseAmount *big.Int) (int64, error) { diff --git a/core/ai.go b/core/ai.go index 9ac86a307..e36260fa6 100644 --- a/core/ai.go +++ b/core/ai.go @@ -14,6 +14,8 @@ import ( "github.com/livepeer/ai-worker/worker" ) +var errPipelineNotAvailable = errors.New("pipeline not available") + type AI interface { TextToImage(context.Context, worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) ImageToImage(context.Context, worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) @@ -41,6 +43,24 @@ func (s JSONRat) String() string { return s.FloatString(2) } +// parsePipelineFromModelID converts a pipeline name to a capability enum. +func PipelineToCapability(pipeline string) (Capability, error) { + if pipeline == "" { + return Capability_Unused, errPipelineNotAvailable + } + + pipelineName := strings.ToUpper(pipeline[:1]) + strings.ReplaceAll(pipeline[1:], "-", " ") + + for cap, desc := range CapabilityNameLookup { + if pipelineName == desc { + return cap, nil + } + } + + // No capability description matches name. + return Capability_Unused, errPipelineNotAvailable +} + type AIModelConfig struct { Pipeline string `json:"pipeline"` ModelID string `json:"model_id"` diff --git a/core/ai_test.go b/core/ai_test.go new file mode 100644 index 000000000..a826e1da4 --- /dev/null +++ b/core/ai_test.go @@ -0,0 +1,20 @@ +package core + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPipelineToCapability(t *testing.T) { + good := "audio-to-text" + bad := "i-love-tests" + + cap, err := PipelineToCapability(good) + assert.Nil(t, err) + assert.Equal(t, cap, Capability_AudioToText) + + cap, err = PipelineToCapability(bad) + assert.Error(t, err) + assert.Equal(t, cap, Capability_Unused) +} diff --git a/core/orchestrator.go b/core/orchestrator.go index 0ec463905..51fb2206e 100644 --- a/core/orchestrator.go +++ b/core/orchestrator.go @@ -317,6 +317,14 @@ func (orch *orchestrator) PriceInfoForCaps(sender ethcommon.Address, manifestID return nil, err } + if !price.Num().IsInt64() || !price.Denom().IsInt64() { + fixedPrice, err := common.PriceToInt64(price) + if err != nil { + return nil, errors.New("price cannot be converted to int64") + } + price = fixedPrice + } + return &net.PriceInfo{ PricePerUnit: price.Num().Int64(), PixelsPerUnit: price.Denom().Int64(), diff --git a/monitor/census.go b/monitor/census.go index 12ab13327..4187d62bd 100644 --- a/monitor/census.go +++ b/monitor/census.go @@ -174,7 +174,7 @@ type ( mMinGasPrice *stats.Float64Measure mMaxGasPrice *stats.Float64Measure mTranscodingPrice *stats.Float64Measure - + mPricePerCapability *stats.Float64Measure // Metrics for calling rewards mRewardCallError *stats.Int64Measure @@ -335,6 +335,7 @@ func InitCensus(nodeType NodeType, version string) { census.mMinGasPrice = stats.Float64("min_gas_price", "MinGasPrice", "gwei") census.mMaxGasPrice = stats.Float64("max_gas_price", "MaxGasPrice", "gwei") census.mTranscodingPrice = stats.Float64("transcoding_price", "TranscodingPrice", "wei") + census.mPricePerCapability = stats.Float64("price_per_capability", "PricePerCapability", "wei") // Metrics for calling rewards census.mRewardCallError = stats.Int64("reward_call_errors", "RewardCallError", "tot") @@ -833,6 +834,13 @@ func InitCensus(nodeType NodeType, version string) { TagKeys: baseTagsWithEthAddr, Aggregation: view.LastValue(), }, + { + Name: "price_per_capability", + Measure: census.mPricePerCapability, + Description: "price per unit per capability", + TagKeys: append([]tag.Key{census.kPipeline, census.kModelName}, baseTags...), + Aggregation: view.LastValue(), + }, // Metrics for calling rewards { @@ -1638,6 +1646,16 @@ func MaxTranscodingPrice(maxPrice *big.Rat) { } } +func MaxPriceForCapability(cap string, modelName string, maxPrice *big.Rat) { + floatWei, _ := maxPrice.Float64() + if err := stats.RecordWithTags(census.ctx, + []tag.Mutator{tag.Insert(census.kPipeline, cap), tag.Insert(census.kModelName, modelName)}, + census.mPricePerCapability.M(floatWei)); err != nil { + + glog.Errorf("Error recording metrics err=%q", err) + } +} + // TicketValueRecv records the ticket value received from a sender for a manifestID func TicketValueRecv(ctx context.Context, sender string, value *big.Rat) { if value.Cmp(big.NewRat(0, 1)) <= 0 { diff --git a/server/ai_session.go b/server/ai_session.go index 2e8c62f7a..527b5e8d3 100644 --- a/server/ai_session.go +++ b/server/ai_session.go @@ -173,11 +173,15 @@ func NewAISessionSelector(cap core.Capability, modelID string, node *core.Livepe suspender := newSuspender() + // Create caps for selector to get maxPrice + warmCaps := newAICapabilities(cap, modelID, true, node.Capabilities.MinVersionConstraint()) + coldCaps := newAICapabilities(cap, modelID, false, node.Capabilities.MinVersionConstraint()) + // The latency score in this context is just the latency of the last completed request for a session // The "good enough" latency score is set to 0.0 so the selector will always select unknown sessions first minLS := 0.0 - warmPool := NewAISessionPool(NewMinLSSelector(stakeRdr, minLS, node.SelectionAlgorithm, node.OrchPerfScore), suspender) - coldPool := NewAISessionPool(NewMinLSSelector(stakeRdr, minLS, node.SelectionAlgorithm, node.OrchPerfScore), suspender) + warmPool := NewAISessionPool(NewMinLSSelector(stakeRdr, minLS, node.SelectionAlgorithm, node.OrchPerfScore, warmCaps), suspender) + coldPool := NewAISessionPool(NewMinLSSelector(stakeRdr, minLS, node.SelectionAlgorithm, node.OrchPerfScore, coldCaps), suspender) sel := &AISessionSelector{ warmPool: warmPool, coldPool: coldPool, @@ -196,6 +200,24 @@ func NewAISessionSelector(cap core.Capability, modelID string, node *core.Livepe return sel, nil } +// newAICapabilities creates a new capabilities object with +func newAICapabilities(cap core.Capability, modelID string, warm bool, minVersion string) *core.Capabilities { + aiCaps := []core.Capability{cap} + capabilityConstraints := core.PerCapabilityConstraints{ + cap: &core.CapabilityConstraints{ + Models: map[string]*core.ModelConstraint{ + modelID: {Warm: warm}, + }, + }, + } + + caps := core.NewCapabilities(aiCaps, nil) + caps.SetPerCapabilityConstraints(capabilityConstraints) + caps.SetMinVersionConstraint(minVersion) + + return caps +} + func (sel *AISessionSelector) Select(ctx context.Context) *AISession { shouldRefreshSelector := func() bool { // Refresh if the # of sessions across warm and cold pools falls below the smaller of the maxRefreshSessionsThreshold and diff --git a/server/broadcast.go b/server/broadcast.go index df854cc48..41a086aaa 100755 --- a/server/broadcast.go +++ b/server/broadcast.go @@ -41,7 +41,7 @@ var maxRefreshSessionsThreshold = 8.0 var recordSegmentsMaxTimeout = 1 * time.Minute var Policy *verification.Policy -var BroadcastCfg = &BroadcastConfig{} +var BroadcastCfg = newBroadcastConfig() var MaxAttempts = 3 var MetadataQueue event.SimpleProducer @@ -56,8 +56,18 @@ var submitMultiSession = func(ctx context.Context, sess *BroadcastSession, seg * var maxTranscodeAttempts = errors.New("hit max transcode attempts") type BroadcastConfig struct { - maxPrice *core.AutoConvertedPrice - mu sync.RWMutex + maxPricePerCapability map[core.Capability]map[string]*core.AutoConvertedPrice + mu sync.RWMutex +} + +func newBroadcastConfig() *BroadcastConfig { + maxPrices := make(map[core.Capability]map[string]*core.AutoConvertedPrice) + models := make(map[string]*core.AutoConvertedPrice) + models["default"] = core.NewFixedPrice(big.NewRat(0, 1)) + maxPrices[core.Capability_Unused] = models + return &BroadcastConfig{ + maxPricePerCapability: maxPrices, + } } type SegFlightMetadata struct { @@ -68,22 +78,82 @@ type SegFlightMetadata struct { func (cfg *BroadcastConfig) MaxPrice() *big.Rat { cfg.mu.RLock() defer cfg.mu.RUnlock() - if cfg.maxPrice == nil { + //base price is capability that won't be set with specific price + if cfg.maxPricePerCapability[core.Capability_Unused]["default"] == nil { return nil } - return cfg.maxPrice.Value() + return cfg.maxPricePerCapability[core.Capability_Unused]["default"].Value() } func (cfg *BroadcastConfig) SetMaxPrice(price *core.AutoConvertedPrice) { cfg.mu.Lock() defer cfg.mu.Unlock() - prevPrice := cfg.maxPrice - cfg.maxPrice = price + prevPrice := cfg.maxPricePerCapability[core.Capability_Unused]["default"] + cfg.maxPricePerCapability[core.Capability_Unused]["default"] = price if prevPrice != nil { prevPrice.Stop() } } +// GetCapabilitiesMaxPrice returns the max price for the given capabilities. +func (cfg *BroadcastConfig) GetCapabilitiesMaxPrice(caps common.CapabilityComparator) *big.Rat { + cfg.mu.RLock() + defer cfg.mu.RUnlock() + if caps == nil { + return cfg.MaxPrice() + } + netCaps := caps.ToNetCapabilities() + price := big.NewRat(0, 1) + for capabilityInt, constraints := range netCaps.Constraints.PerCapability { + for modelID := range constraints.Models { + if capPrice := cfg.getCapabilityMaxPrice(core.Capability(capabilityInt), modelID); capPrice != nil { + price = price.Add(price, capPrice) + } + } + } + + // If no prices set per model, return maxPrice + if price.Sign() == 0 { + return cfg.MaxPrice() + } + + return price +} + +func (cfg *BroadcastConfig) getCapabilityMaxPrice(cap core.Capability, modelID string) *big.Rat { + cfg.mu.RLock() + defer cfg.mu.RUnlock() + models, ok := cfg.maxPricePerCapability[cap] + if !ok { + // No price set for capability + return nil + } + if price, modelOk := models[modelID]; modelOk { + return price.Value() + } + if defaultPrice, hasDefault := models["default"]; hasDefault { + return defaultPrice.Value() + } + + // No price set for the specific model or default + return nil +} + +func (cfg *BroadcastConfig) SetCapabilityMaxPrice(cap core.Capability, modelID string, newPrice *core.AutoConvertedPrice) { + cfg.mu.RLock() + defer cfg.mu.RUnlock() + if _, ok := cfg.maxPricePerCapability[cap]; !ok { + cfg.maxPricePerCapability[cap] = make(map[string]*core.AutoConvertedPrice) + } + + // Stop previous price subscription if it exists. + if prevPrice, exists := cfg.maxPricePerCapability[cap][modelID]; exists && prevPrice != nil { + prevPrice.Stop() + } + + cfg.maxPricePerCapability[cap][modelID] = newPrice +} + type sessionsCreator func() ([]*BroadcastSession, error) type SessionPool struct { mid core.ManifestID @@ -476,8 +546,8 @@ func NewSessionManager(ctx context.Context, node *core.LivepeerNode, params *cor bsm := &BroadcastSessionsManager{ mid: params.ManifestID, VerificationFreq: params.VerificationFreq, - trustedPool: NewSessionPool(params.ManifestID, int(trustedPoolSize), trustedNumOrchs, susTrusted, createSessionsTrusted, NewMinLSSelector(stakeRdr, 1.0, node.SelectionAlgorithm, node.OrchPerfScore)), - untrustedPool: NewSessionPool(params.ManifestID, int(untrustedPoolSize), untrustedNumOrchs, susUntrusted, createSessionsUntrusted, NewMinLSSelector(stakeRdr, 1.0, node.SelectionAlgorithm, node.OrchPerfScore)), + trustedPool: NewSessionPool(params.ManifestID, int(trustedPoolSize), trustedNumOrchs, susTrusted, createSessionsTrusted, NewMinLSSelector(stakeRdr, 1.0, node.SelectionAlgorithm, node.OrchPerfScore, params.Capabilities)), + untrustedPool: NewSessionPool(params.ManifestID, int(untrustedPoolSize), untrustedNumOrchs, susUntrusted, createSessionsUntrusted, NewMinLSSelector(stakeRdr, 1.0, node.SelectionAlgorithm, node.OrchPerfScore, params.Capabilities)), } bsm.trustedPool.refreshSessions(ctx) bsm.untrustedPool.refreshSessions(ctx) diff --git a/server/handlers.go b/server/handlers.go index e95315654..b05fee624 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -199,6 +199,74 @@ func setBroadcastConfigHandler() http.Handler { }) } +func (s *LivepeerServer) setMaxPriceForCapability() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if s.LivepeerNode.NodeType == core.BroadcasterNode { + maxPricePerUnit := r.FormValue("maxPricePerUnit") + pixelsPerUnit := r.FormValue("pixelsPerUnit") + currency := r.FormValue("currency") + pipeline := r.FormValue("pipeline") + modelID := r.FormValue("modelID") + + if pipeline == "" || modelID == "" { + respond400(w, "pipeline and modelID must be set") + return + } + + cap, err := core.PipelineToCapability(pipeline) + if err != nil { + respond400(w, "pipeline not supported") + return + } + + // set max price + if maxPricePerUnit != "" && pixelsPerUnit != "" { + pr, ok := new(big.Rat).SetString(maxPricePerUnit) + if !ok { + respond400(w, fmt.Sprintf("Error parsing pricePerUnit value: %s", maxPricePerUnit)) + return + } + px, ok := new(big.Rat).SetString(pixelsPerUnit) + if !ok { + respond400(w, fmt.Sprintf("Error parsing pixelsPerUnit value: %s", pixelsPerUnit)) + return + } + if px.Sign() <= 0 { + respond400(w, fmt.Sprintf("pixels per unit must be greater than 0, provided %v", pixelsPerUnit)) + return + } + pricePerPixel := new(big.Rat).Quo(pr, px) + + var autoPrice *core.AutoConvertedPrice + if pricePerPixel.Sign() > 0 { + var err error + autoPrice, err = core.NewAutoConvertedPrice(currency, pricePerPixel, func(price *big.Rat) { + if monitor.Enabled { + monitor.MaxPriceForCapability(core.CapabilityNameLookup[cap], modelID, price) + } + glog.Infof("Maximum price per unit set to %v wei for capability=%v model_id=%v", price.FloatString(3), pipeline, modelID) + }) + if err != nil { + respond400(w, errors.Wrap(err, "error converting price").Error()) + return + } + + BroadcastCfg.SetCapabilityMaxPrice(cap, modelID, autoPrice) + respondOk(w, nil) + } else { + respond400(w, fmt.Sprintf("pricePerPixel needs to be > 0: %v", pricePerPixel.FloatString(3))) + } + } else { + respond400(w, "maxPricePerUnit and pixelsPerUnit need to be set") + return + } + } else { + respond400(w, "Node must be gateway node to set max price per capability") + return + } + }) +} + func getBroadcastConfigHandler() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var pNames []string diff --git a/server/handlers_test.go b/server/handlers_test.go index d191d0a75..5a1939a39 100644 --- a/server/handlers_test.go +++ b/server/handlers_test.go @@ -256,10 +256,143 @@ func TestSetBroadcastConfigHandler_Success(t *testing.T) { assert.Equal(profiles, BroadcastJobVideoProfiles) } +func TestSetMaxPriceForCapabilityHandler(t *testing.T) { + assert := assert.New(t) + s := stubServer() + s.LivepeerNode.NodeType = core.BroadcasterNode + + handler := s.setMaxPriceForCapability() + + //set default max price + basePrice, _ := core.NewAutoConvertedPrice("WEI", big.NewRat(10, 1), nil) + BroadcastCfg.SetMaxPrice(basePrice) + + //set price per unit for specific pipeline + p1, _ := core.NewAutoConvertedPrice("WEI", big.NewRat(1, 1), nil) + p2, _ := core.NewAutoConvertedPrice("WEI", big.NewRat(2, 1), nil) + p1_pipeline := "text-to-image" + p1_pipeline_cap, _ := core.PipelineToCapability(p1_pipeline) + p1_modelID := "default" + + p2_pipeline := "image-to-image" + p2_pipeline_cap, _ := core.PipelineToCapability(p2_pipeline) + p2_modelID := "default" + + status1, _ := postForm(handler, url.Values{ + "maxPricePerUnit": {"1"}, + "pixelsPerUnit": {"1"}, + "currency": {"WEI"}, + "pipeline": {p1_pipeline}, + "modelID": {p1_modelID}, + }) + + assert.Equal(http.StatusOK, status1) + assert.Equal(p1.Value(), BroadcastCfg.getCapabilityMaxPrice(p1_pipeline_cap, p1_modelID)) + + status2, _ := postForm(handler, url.Values{ + "maxPricePerUnit": {"2"}, + "pixelsPerUnit": {"1"}, + "currency": {"WEI"}, + "pipeline": {p2_pipeline}, + "modelID": {p2_modelID}, + }) + + assert.Equal(http.StatusOK, status2) + assert.Equal(p2.Value(), BroadcastCfg.getCapabilityMaxPrice(p2_pipeline_cap, p1_modelID)) + + p1_modelID = "stabilityai/sd-turbo" + status1, _ = postForm(handler, url.Values{ + "maxPricePerUnit": {"100"}, + "pixelsPerUnit": {"1"}, + "currency": {"WEI"}, + "pipeline": {p1_pipeline}, + "modelID": {p1_modelID}, + }) + assert.Equal(http.StatusOK, status1) + assert.NotEqual(p1.Value(), BroadcastCfg.getCapabilityMaxPrice(p1_pipeline_cap, p1_modelID)) + assert.Equal(big.NewRat(100, 1), BroadcastCfg.getCapabilityMaxPrice(p1_pipeline_cap, p1_modelID)) +} + +func TestSetMaxPriceForCapabilityHandler_NotGateway(t *testing.T) { + assert := assert.New(t) + s := stubServer() + s.LivepeerNode.NodeType = core.OrchestratorNode + + handler := s.setMaxPriceForCapability() + + status, _ := postForm(handler, url.Values{ + "maxPricePerUnit": {"10"}, + "pixelsPerUnit": {"1"}, + "currency": {"WEI"}, + "pipeline": {"text-to-image"}, + "modelID": {"default"}, + }) + + assert.Equal(http.StatusBadRequest, status) +} + +func TestSetMaxPriceForCapabilityHandler_WrongInput(t *testing.T) { + assert := assert.New(t) + s := stubServer() + s.LivepeerNode.NodeType = core.BroadcasterNode + + handler := s.setMaxPriceForCapability() + + //pricePerUnit is not int + status1, _ := postForm(handler, url.Values{ + "maxPricePerUnit": {"a"}, + "pixelsPerUnit": {"1"}, + "currency": {"WEI"}, + "pipeline": {"text-to-image"}, + "modelID": {"default"}, + }) + assert.Equal(http.StatusBadRequest, status1) + + //pixelsPerUnit is not int + status2, _ := postForm(handler, url.Values{ + "maxPricePerUnit": {"1"}, + "pixelsPerUnit": {"a"}, + "currency": {"WEI"}, + "pipeline": {"text-to-image"}, + "modelID": {"default"}, + }) + assert.Equal(http.StatusBadRequest, status2) + + //pipeline is not set + status4, _ := postForm(handler, url.Values{ + "maxPricePerUnit": {"1"}, + "pixelsPerUnit": {"1"}, + "currency": {"WEI"}, + "pipeline": {""}, + "modelID": {"default"}, + }) + assert.Equal(http.StatusBadRequest, status4) + + //modelID is not set + status5, _ := postForm(handler, url.Values{ + "maxPricePerUnit": {"1"}, + "pixelsPerUnit": {"1"}, + "currency": {"WEI"}, + "pipeline": {"text-to-image"}, + "modelID": {""}, + }) + assert.Equal(http.StatusBadRequest, status5) + + //pipeline not supported + status6, _ := postForm(handler, url.Values{ + "maxPricePerUnit": {"1"}, + "pixelsPerUnit": {"1"}, + "currency": {"WEI"}, + "pipeline": {"cool-new-pipeline"}, + "modelID": {"default"}, + }) + assert.Equal(http.StatusBadRequest, status6) +} + func TestGetBroadcastConfigHandler(t *testing.T) { assert := assert.New(t) - BroadcastCfg.maxPrice = core.NewFixedPrice(big.NewRat(1, 2)) + BroadcastCfg.SetMaxPrice(core.NewFixedPrice(big.NewRat(1, 2))) BroadcastJobVideoProfiles = []ffmpeg.VideoProfile{ ffmpeg.VideoProfileLookup["P240p25fps16x9"], } diff --git a/server/mediaserver.go b/server/mediaserver.go index 1e1b1be15..9b0d0cad4 100644 --- a/server/mediaserver.go +++ b/server/mediaserver.go @@ -537,7 +537,7 @@ func (s *LivepeerServer) registerConnection(ctx context.Context, rtmpStrm stream stakeRdr = &storeStakeReader{store: s.LivepeerNode.Database} } selFactory := func() BroadcastSessionsSelector { - return NewMinLSSelector(stakeRdr, SELECTOR_LATENCY_SCORE_THRESHOLD, s.LivepeerNode.SelectionAlgorithm, s.LivepeerNode.OrchPerfScore) + return NewMinLSSelector(stakeRdr, SELECTOR_LATENCY_SCORE_THRESHOLD, s.LivepeerNode.SelectionAlgorithm, s.LivepeerNode.OrchPerfScore, params.Capabilities) } // safe, because other goroutines should be waiting on initializing channel diff --git a/server/rpc_test.go b/server/rpc_test.go index c0832a0c4..0ede88b26 100644 --- a/server/rpc_test.go +++ b/server/rpc_test.go @@ -25,6 +25,7 @@ import ( "github.com/golang/protobuf/proto" + "github.com/livepeer/ai-worker/worker" "github.com/livepeer/go-livepeer/common" "github.com/livepeer/go-livepeer/core" "github.com/livepeer/go-livepeer/crypto" @@ -183,6 +184,27 @@ func (r *stubOrchestrator) TranscoderResults(job int64, res *core.RemoteTranscod func (r *stubOrchestrator) TranscoderSecret() string { return "" } +func (r *stubOrchestrator) PriceInfoForCaps(sender ethcommon.Address, manifestID core.ManifestID, caps *net.Capabilities) (*net.PriceInfo, error) { + return &net.PriceInfo{PricePerUnit: 4, PixelsPerUnit: 1}, nil +} +func (r *stubOrchestrator) TextToImage(ctx context.Context, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) { + return nil, nil +} +func (r *stubOrchestrator) ImageToImage(ctx context.Context, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { + return nil, nil +} +func (r *stubOrchestrator) ImageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { + return nil, nil +} +func (r *stubOrchestrator) Upscale(ctx context.Context, req worker.UpscaleMultipartRequestBody) (*worker.ImageResponse, error) { + return nil, nil +} +func (r *stubOrchestrator) AudioToText(ctx context.Context, req worker.AudioToTextMultipartRequestBody) (*worker.TextResponse, error) { + return nil, nil +} +func (r *stubOrchestrator) CheckAICapacity(pipeline, modelID string) bool { + return true +} func stubBroadcaster2() *stubOrchestrator { return newStubOrchestrator() // lazy; leverage subtyping for interface commonalities } @@ -192,7 +214,7 @@ func TestRPCTranscoderReq(t *testing.T) { o := newStubOrchestrator() b := stubBroadcaster2() - req, err := genOrchestratorReq(b) + req, err := genOrchestratorReq(b, nil) if err != nil { t.Error("Unable to create orchestrator req ", req) } @@ -224,7 +246,7 @@ func TestRPCTranscoderReq(t *testing.T) { // error signing b.signErr = fmt.Errorf("Signing error") - _, err = genOrchestratorReq(b) + _, err = genOrchestratorReq(b, nil) if err == nil { t.Error("Did not expect to generate a orchestrator request with invalid address") } @@ -1345,7 +1367,27 @@ func (o *mockOrchestrator) AuthToken(sessionID string, expiration int64) *net.Au } return nil } - +func (r *mockOrchestrator) PriceInfoForCaps(sender ethcommon.Address, manifestID core.ManifestID, caps *net.Capabilities) (*net.PriceInfo, error) { + return &net.PriceInfo{PricePerUnit: 4, PixelsPerUnit: 1}, nil +} +func (r *mockOrchestrator) TextToImage(ctx context.Context, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) { + return nil, nil +} +func (r *mockOrchestrator) ImageToImage(ctx context.Context, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { + return nil, nil +} +func (r *mockOrchestrator) ImageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { + return nil, nil +} +func (r *mockOrchestrator) Upscale(ctx context.Context, req worker.UpscaleMultipartRequestBody) (*worker.ImageResponse, error) { + return nil, nil +} +func (r *mockOrchestrator) AudioToText(ctx context.Context, req worker.AudioToTextMultipartRequestBody) (*worker.TextResponse, error) { + return nil, nil +} +func (r *mockOrchestrator) CheckAICapacity(pipeline, modelID string) bool { + return true +} func defaultTicketParams() *net.TicketParams { return &net.TicketParams{ Recipient: pm.RandBytes(123), diff --git a/server/selection.go b/server/selection.go index c827034db..9904d9a7b 100644 --- a/server/selection.go +++ b/server/selection.go @@ -99,12 +99,13 @@ type MinLSSelector struct { stakeRdr stakeReader selectionAlgorithm common.SelectionAlgorithm perfScore *common.PerfScore + capabilities common.CapabilityComparator minLS float64 } // NewMinLSSelector returns an instance of MinLSSelector configured with a good enough latency score -func NewMinLSSelector(stakeRdr stakeReader, minLS float64, selectionAlgorithm common.SelectionAlgorithm, perfScore *common.PerfScore) *MinLSSelector { +func NewMinLSSelector(stakeRdr stakeReader, minLS float64, selectionAlgorithm common.SelectionAlgorithm, perfScore *common.PerfScore, capabilities common.CapabilityComparator) *MinLSSelector { knownSessions := &sessHeap{} heap.Init(knownSessions) @@ -113,6 +114,7 @@ func NewMinLSSelector(stakeRdr stakeReader, minLS float64, selectionAlgorithm co stakeRdr: stakeRdr, selectionAlgorithm: selectionAlgorithm, perfScore: perfScore, + capabilities: capabilities, minLS: minLS, } } @@ -185,7 +187,8 @@ func (s *MinLSSelector) selectUnknownSession(ctx context.Context) *BroadcastSess prices[addr] = big.NewRat(pi.PricePerUnit, pi.PixelsPerUnit) } } - maxPrice := BroadcastCfg.MaxPrice() + + maxPrice := BroadcastCfg.GetCapabilitiesMaxPrice(s.capabilities) stakes, err := s.stakeRdr.Stakes(addrs) if err != nil { diff --git a/server/selection_test.go b/server/selection_test.go index aa936ddb0..1241875b2 100644 --- a/server/selection_test.go +++ b/server/selection_test.go @@ -160,7 +160,7 @@ func TestSessHeap(t *testing.T) { func TestMinLSSelector(t *testing.T) { assert := assert.New(t) - sel := NewMinLSSelector(nil, 1.0, stubSelectionAlgorithm{}, nil) + sel := NewMinLSSelector(nil, 1.0, stubSelectionAlgorithm{}, nil, nil) assert.Zero(sel.Size()) sessions := []*BroadcastSession{ @@ -236,7 +236,7 @@ func TestMinLSSelector(t *testing.T) { func TestMinLSSelector_RemoveUnknownSession(t *testing.T) { assert := assert.New(t) - sel := NewMinLSSelector(nil, 1.0, stubSelectionAlgorithm{}, nil) + sel := NewMinLSSelector(nil, 1.0, stubSelectionAlgorithm{}, nil, nil) // Use ManifestID to identify each session sessions := []*BroadcastSession{ @@ -337,7 +337,7 @@ func TestMinLSSelector_SelectUnknownSession(t *testing.T) { if tt.perfScores != nil { perfScore = &common.PerfScore{Scores: tt.perfScores} } - sel := NewMinLSSelector(stakeRdr, 1.0, selAlg, perfScore) + sel := NewMinLSSelector(stakeRdr, 1.0, selAlg, perfScore, nil) sel.Add(tt.unknownSessions) sess := sel.selectUnknownSession(context.TODO()) @@ -368,7 +368,7 @@ func session(recipientAddr string) *BroadcastSession { } func TestMinLSSelector_SelectUnknownSession_NilStakeReader(t *testing.T) { - sel := NewMinLSSelector(nil, 1.0, stubSelectionAlgorithm{}, nil) + sel := NewMinLSSelector(nil, 1.0, stubSelectionAlgorithm{}, nil, nil) sessions := make([]*BroadcastSession, 10) for i := 0; i < 10; i++ { diff --git a/server/webserver.go b/server/webserver.go index 9cabbcb97..cf1578b31 100644 --- a/server/webserver.go +++ b/server/webserver.go @@ -49,6 +49,7 @@ func (s *LivepeerServer) cliWebServerHandlers(bindAddr string) *http.ServeMux { mux.Handle("/setBroadcastConfig", mustHaveFormParams(setBroadcastConfigHandler())) mux.Handle("/getBroadcastConfig", getBroadcastConfigHandler()) mux.Handle("/getAvailableTranscodingOptions", getAvailableTranscodingOptionsHandler()) + mux.Handle("/setMaxPriceForCapability", mustHaveFormParams(s.setMaxPriceForCapability(), "maxPricePerUnit", "pixelsPerUnit", "currency", "pipeline", "modelID")) // Rounds mux.Handle("/currentRound", currentRoundHandler(client)) From d4e4b6d1ed9387162b583c015ed478a551eb16ad Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Sat, 24 Aug 2024 00:35:01 +0200 Subject: [PATCH 194/203] chore(ai): release version v0.7.7-ai.4 This commit released the v0.7.7-ai.3 from the AI subnet software. --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index b50eecfd5..fed880115 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.7.7-ai.3 +0.7.7-ai.4 From b5d351f7017a30afcfb467c7c88ea70d423e1e5d Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Sat, 24 Aug 2024 00:38:49 +0200 Subject: [PATCH 195/203] chore(ai): release version v0.7.8-ai.1 This commit released the v0.7.8-ai.1 from the AI subnet software. --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index fed880115..31f146361 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.7.7-ai.4 +0.7.8-ai.1 From a4116f78c1094226b1adca5365c846068d374727 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Sat, 24 Aug 2024 01:54:13 +0200 Subject: [PATCH 196/203] refactor(ai): apply small code improvement (#3152) This commit applies a small code improvement. --- common/util.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/common/util.go b/common/util.go index 6f30b57f4..a568cb61d 100644 --- a/common/util.go +++ b/common/util.go @@ -359,13 +359,15 @@ func FixedToPrice(price int64) *big.Rat { return big.NewRat(price, priceScalingFactor) } +// PriceToInt64 converts a *big.Rat to an *int64 if possible, otherwise returns an error. func PriceToInt64(price *big.Rat) (*big.Rat, error) { fixed := new(big.Int).Div(price.Num(), price.Denom()) - if fixed.IsInt64() { - return big.NewRat(fixed.Int64(), int64(1)), nil - } else { - return nil, errors.New("price cannot convert to int64") + if !fixed.IsInt64() { + return nil, errors.New("price cannot be converted to int64") } + + // Return a new big.Rat with an int64 numerator and a denominator of 1. + return big.NewRat(fixed.Int64(), 1), nil } // BaseTokenAmountToFixed converts the base amount of a token (i.e. ETH/LPT) represented as a big.Int into a fixed point number represented From 54b4de234398c8992bd634bf97169961332f4f62 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Sat, 24 Aug 2024 01:57:40 +0200 Subject: [PATCH 197/203] refactor(ai): remove redundant comment (#3153) This commit removes a redundant command which I introduced in the last commit. --- common/util.go | 1 - 1 file changed, 1 deletion(-) diff --git a/common/util.go b/common/util.go index a568cb61d..a798e10a1 100644 --- a/common/util.go +++ b/common/util.go @@ -366,7 +366,6 @@ func PriceToInt64(price *big.Rat) (*big.Rat, error) { return nil, errors.New("price cannot be converted to int64") } - // Return a new big.Rat with an int64 numerator and a denominator of 1. return big.NewRat(fixed.Int64(), 1), nil } From 3316e352c8f537c5d7b374e8ea5e00ded6fe853b Mon Sep 17 00:00:00 2001 From: PSchroedl Date: Wed, 4 Sep 2024 03:28:02 -0700 Subject: [PATCH 198/203] feat(ai): add segment anything 2 pipeline image version(#3131) This commit adds support for the new [segment anything 2](https://ai.meta.com/sam2/) pipeline (SAM2) that was added to the AI-worker in [this pull request](https://github.com/livepeer/ai-worker/pull/185). While the new SAM pipeline can also do video segmentation this will be done in a subsequent pull request. Co-authored-by: John | Elite Encoder Co-authored-by: Peter Schroedl Co-authored-by: Rick Staa --- cmd/livepeer/starter/starter.go | 19 +++++- core/ai.go | 1 + core/capabilities.go | 3 + core/orchestrator.go | 8 +++ go.mod | 16 ++--- go.sum | 32 +++++----- server/ai_http.go | 45 +++++++++++++ server/ai_mediaserver.go | 54 ++++++++++++++++ server/ai_process.go | 108 ++++++++++++++++++++++++++++++++ server/rpc.go | 1 + server/rpc_test.go | 6 ++ 11 files changed, 267 insertions(+), 26 deletions(-) diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index 5a8bb5bb2..00c23ec3d 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -1227,10 +1227,11 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { } } - // If the config contains a URL we call Warm() anyway because AIWorker will just register - // the endpoint for an external container if config.Warm || config.URL != "" { + // Register external container endpoint if URL is provided. endpoint := worker.RunnerEndpoint{URL: config.URL, Token: config.Token} + + // Warm the AI worker container or register the endpoint. if err := n.AIWorker.Warm(ctx, config.Pipeline, config.ModelID, endpoint, config.OptimizationFlags); err != nil { glog.Errorf("Error AI worker warming %v container: %v", config.Pipeline, err) return @@ -1313,6 +1314,20 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { if *cfg.Network != "offchain" { n.SetBasePriceForCap("default", core.Capability_AudioToText, config.ModelID, autoPrice) } + case "segment-anything-2": + _, ok := capabilityConstraints[core.Capability_SegmentAnything2] + if !ok { + aiCaps = append(aiCaps, core.Capability_SegmentAnything2) + capabilityConstraints[core.Capability_SegmentAnything2] = &core.CapabilityConstraints{ + Models: make(map[string]*core.ModelConstraint), + } + } + + capabilityConstraints[core.Capability_SegmentAnything2].Models[config.ModelID] = modelConstraint + + if *cfg.Network != "offchain" { + n.SetBasePriceForCap("default", core.Capability_SegmentAnything2, config.ModelID, autoPrice) + } } if len(aiCaps) > 0 { diff --git a/core/ai.go b/core/ai.go index e36260fa6..887e1f8c8 100644 --- a/core/ai.go +++ b/core/ai.go @@ -22,6 +22,7 @@ type AI interface { ImageToVideo(context.Context, worker.ImageToVideoMultipartRequestBody) (*worker.VideoResponse, error) Upscale(context.Context, worker.UpscaleMultipartRequestBody) (*worker.ImageResponse, error) AudioToText(context.Context, worker.AudioToTextMultipartRequestBody) (*worker.TextResponse, error) + SegmentAnything2(context.Context, worker.SegmentAnything2MultipartRequestBody) (*worker.MasksResponse, error) Warm(context.Context, string, string, worker.RunnerEndpoint, worker.OptimizationFlags) error Stop(context.Context) error HasCapacity(pipeline, modelID string) bool diff --git a/core/capabilities.go b/core/capabilities.go index b02e13780..734404ca8 100644 --- a/core/capabilities.go +++ b/core/capabilities.go @@ -78,6 +78,7 @@ const ( Capability_ImageToVideo Capability_Upscale Capability_AudioToText + Capability_SegmentAnything2 ) var CapabilityNameLookup = map[Capability]string{ @@ -114,6 +115,7 @@ var CapabilityNameLookup = map[Capability]string{ Capability_ImageToVideo: "Image to video", Capability_Upscale: "Upscale", Capability_AudioToText: "Audio to text", + Capability_SegmentAnything2: "Segment Anything 2", } var CapabilityTestLookup = map[Capability]CapabilityTest{ @@ -204,6 +206,7 @@ func OptionalCapabilities() []Capability { Capability_ImageToVideo, Capability_Upscale, Capability_AudioToText, + Capability_SegmentAnything2, } } diff --git a/core/orchestrator.go b/core/orchestrator.go index 51fb2206e..ba9f470bd 100644 --- a/core/orchestrator.go +++ b/core/orchestrator.go @@ -130,6 +130,10 @@ func (orch *orchestrator) AudioToText(ctx context.Context, req worker.AudioToTex return orch.node.AudioToText(ctx, req) } +func (orch *orchestrator) SegmentAnything2(ctx context.Context, req worker.SegmentAnything2MultipartRequestBody) (*worker.MasksResponse, error) { + return orch.node.SegmentAnything2(ctx, req) +} + func (orch *orchestrator) ProcessPayment(ctx context.Context, payment net.Payment, manifestID ManifestID) error { if orch.node == nil || orch.node.Recipient == nil { return nil @@ -963,6 +967,10 @@ func (n *LivepeerNode) AudioToText(ctx context.Context, req worker.AudioToTextMu return n.AIWorker.AudioToText(ctx, req) } +func (n *LivepeerNode) SegmentAnything2(ctx context.Context, req worker.SegmentAnything2MultipartRequestBody) (*worker.MasksResponse, error) { + return n.AIWorker.SegmentAnything2(ctx, req) +} + func (n *LivepeerNode) imageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { // We might support generating more than one video in the future (i.e. multiple input images/prompts) numVideos := 1 diff --git a/go.mod b/go.mod index bfbe2c494..b0fd01bd9 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/golang/protobuf v1.5.4 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.1.2 + github.com/livepeer/ai-worker v0.2.0 github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240819180416-f87352959b85 @@ -31,7 +31,7 @@ require ( github.com/urfave/cli v1.22.12 go.opencensus.io v0.24.0 go.uber.org/goleak v1.3.0 - golang.org/x/net v0.25.0 + golang.org/x/net v0.28.0 google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.1 pgregory.net/rapid v1.1.0 @@ -215,15 +215,15 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/crypto v0.23.0 // indirect + golang.org/x/crypto v0.26.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/mod v0.17.0 // indirect + golang.org/x/mod v0.20.0 // indirect golang.org/x/oauth2 v0.20.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.23.0 // indirect + golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.21.0 // indirect + golang.org/x/tools v0.24.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.125.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index 6bcdec2fe..3299db083 100644 --- a/go.sum +++ b/go.sum @@ -623,8 +623,8 @@ github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4n github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/livepeer/ai-worker v0.1.2 h1:I73J4zJYad95QE1JFSrqrjKKCTqLHypDcoPq/zZM5aw= -github.com/livepeer/ai-worker v0.1.2/go.mod h1:Xlnb0nFG2VsGeMG9hZmReVQXeFt0Dv28ODiUT2ooyLE= +github.com/livepeer/ai-worker v0.2.0 h1:u6m3nVQnisqWn2nMhgTgKLQby7VDAEp5xmeHt7Res/Y= +github.com/livepeer/ai-worker v0.2.0/go.mod h1:91lMzkzVuwR9kZ0EzXwf+7yVhLaNVmYAfmBtn7t3cQA= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= @@ -1024,8 +1024,8 @@ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1060,8 +1060,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= +golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1108,8 +1108,8 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1132,8 +1132,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1210,8 +1210,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1226,8 +1226,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1288,8 +1288,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= -golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/server/ai_http.go b/server/ai_http.go index 6b007e0c1..a11164bc9 100644 --- a/server/ai_http.go +++ b/server/ai_http.go @@ -44,6 +44,7 @@ func startAIServer(lp lphttp) error { lp.transRPC.Handle("/image-to-video", oapiReqValidator(lp.ImageToVideo())) lp.transRPC.Handle("/upscale", oapiReqValidator(lp.Upscale())) lp.transRPC.Handle("/audio-to-text", oapiReqValidator(lp.AudioToText())) + lp.transRPC.Handle("/segment-anything-2", oapiReqValidator(lp.SegmentAnything2())) return nil } @@ -157,6 +158,29 @@ func (h *lphttp) AudioToText() http.Handler { }) } +func (h *lphttp) SegmentAnything2() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + orch := h.orchestrator + + remoteAddr := getRemoteAddr(r) + ctx := clog.AddVal(r.Context(), clog.ClientIP, remoteAddr) + + multiRdr, err := r.MultipartReader() + if err != nil { + respondWithError(w, err.Error(), http.StatusBadRequest) + return + } + + var req worker.SegmentAnything2MultipartRequestBody + if err := runtime.BindMultipart(&req, *multiRdr); err != nil { + respondWithError(w, err.Error(), http.StatusInternalServerError) + return + } + + handleAIRequest(ctx, w, r, orch, req) + }) +} + func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request, orch Orchestrator, req interface{}) { payment, err := getPayment(r.Header.Get(paymentHeader)) if err != nil { @@ -281,6 +305,25 @@ func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request return } outPixels *= 1000 // Convert to milliseconds + case worker.SegmentAnything2MultipartRequestBody: + pipeline = "segment-anything-2" + cap = core.Capability_SegmentAnything2 + modelID = *v.ModelId + submitFn = func(ctx context.Context) (interface{}, error) { + return orch.SegmentAnything2(ctx, v) + } + + imageRdr, err := v.Image.Reader() + if err != nil { + respondWithError(w, err.Error(), http.StatusBadRequest) + return + } + config, _, err := image.DecodeConfig(imageRdr) + if err != nil { + respondWithError(w, err.Error(), http.StatusBadRequest) + return + } + outPixels = int64(config.Height) * int64(config.Width) default: respondWithError(w, "Unknown request type", http.StatusBadRequest) return @@ -352,6 +395,8 @@ func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request if err == nil { latencyScore = CalculateAudioToTextLatencyScore(took, durationSeconds) } + case worker.SegmentAnything2MultipartRequestBody: + latencyScore = CalculateSegmentAnything2LatencyScore(took, outPixels) } var pricePerAIUnit float64 diff --git a/server/ai_mediaserver.go b/server/ai_mediaserver.go index 29d33b9fe..a600f3b17 100644 --- a/server/ai_mediaserver.go +++ b/server/ai_mediaserver.go @@ -69,6 +69,7 @@ func startAIMediaServer(ls *LivepeerServer) error { ls.HTTPMux.Handle("/image-to-video", oapiReqValidator(ls.ImageToVideo())) ls.HTTPMux.Handle("/image-to-video/result", ls.ImageToVideoResult()) ls.HTTPMux.Handle("/audio-to-text", oapiReqValidator(ls.AudioToText())) + ls.HTTPMux.Handle("/segment-anything-2", oapiReqValidator(ls.SegmentAnything2())) return nil } @@ -374,6 +375,59 @@ func (ls *LivepeerServer) AudioToText() http.Handler { }) } +func (ls *LivepeerServer) SegmentAnything2() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + remoteAddr := getRemoteAddr(r) + ctx := clog.AddVal(r.Context(), clog.ClientIP, remoteAddr) + requestID := string(core.RandomManifestID()) + ctx = clog.AddVal(ctx, "request_id", requestID) + + multiRdr, err := r.MultipartReader() + if err != nil { + respondJsonError(ctx, w, err, http.StatusBadRequest) + return + } + + var req worker.SegmentAnything2MultipartRequestBody + if err := runtime.BindMultipart(&req, *multiRdr); err != nil { + respondJsonError(ctx, w, err, http.StatusBadRequest) + return + } + + clog.V(common.VERBOSE).Infof(ctx, "Received SegmentAnything2 request; image_size=%v model_id=%v", req.Image.FileSize(), *req.ModelId) + + params := aiRequestParams{ + node: ls.LivepeerNode, + os: drivers.NodeStorage.NewSession(requestID), + sessManager: ls.AISessionManager, + } + + start := time.Now() + resp, err := processSegmentAnything2(ctx, params, req) + if err != nil { + var serviceUnavailableErr *ServiceUnavailableError + var badRequestErr *BadRequestError + if errors.As(err, &serviceUnavailableErr) { + respondJsonError(ctx, w, err, http.StatusServiceUnavailable) + return + } + if errors.As(err, &badRequestErr) { + respondJsonError(ctx, w, err, http.StatusBadRequest) + return + } + respondJsonError(ctx, w, err, http.StatusInternalServerError) + return + } + + took := time.Since(start) + clog.V(common.VERBOSE).Infof(ctx, "Processed SegmentAnything2 request model_id=%v took=%v", *req.ModelId, took) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(resp) + }) +} + func (ls *LivepeerServer) ImageToVideoResult() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { remoteAddr := getRemoteAddr(r) diff --git a/server/ai_process.go b/server/ai_process.go index a8e884f68..d69beb061 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -31,6 +31,7 @@ const defaultImageToImageModelID = "stabilityai/sdxl-turbo" const defaultImageToVideoModelID = "stabilityai/stable-video-diffusion-img2vid-xt" const defaultUpscaleModelID = "stabilityai/stable-diffusion-x4-upscaler" const defaultAudioToTextModelID = "openai/whisper-large-v3" +const defaultSegmentAnything2ModelID = "facebook/sam2-hiera-large" type ServiceUnavailableError struct { err error @@ -586,6 +587,104 @@ func submitUpscale(ctx context.Context, params aiRequestParams, sess *AISession, return resp.JSON200, nil } +// CalculateSegmentAnything2LatencyScore computes the time taken per pixel for a segment-anything-2 request. +func CalculateSegmentAnything2LatencyScore(took time.Duration, outPixels int64) float64 { + if outPixels <= 0 { + return 0 + } + + return took.Seconds() / float64(outPixels) +} + +func processSegmentAnything2(ctx context.Context, params aiRequestParams, req worker.SegmentAnything2MultipartRequestBody) (*worker.MasksResponse, error) { + resp, err := processAIRequest(ctx, params, req) + if err != nil { + return nil, err + } + + txtResp := resp.(*worker.MasksResponse) + + return txtResp, nil +} + +func submitSegmentAnything2(ctx context.Context, params aiRequestParams, sess *AISession, req worker.BodySegmentAnything2SegmentAnything2Post) (*worker.MasksResponse, error) { + var buf bytes.Buffer + mw, err := worker.NewSegmentAnything2MultipartWriter(&buf, req) + if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "segment anything 2", *req.ModelId, sess.OrchestratorInfo) + } + return nil, err + } + + client, err := worker.NewClientWithResponses(sess.Transcoder(), worker.WithHTTPClient(httpClient)) + if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "segment anything 2", *req.ModelId, sess.OrchestratorInfo) + } + return nil, err + } + + imageRdr, err := req.Image.Reader() + if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "segment anything 2", *req.ModelId, sess.OrchestratorInfo) + } + return nil, err + } + config, _, err := image.DecodeConfig(imageRdr) + if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "segment anything 2", *req.ModelId, sess.OrchestratorInfo) + } + return nil, err + } + outPixels := int64(config.Height) * int64(config.Width) + + setHeaders, balUpdate, err := prepareAIPayment(ctx, sess, outPixels) + if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "segment anything 2", *req.ModelId, sess.OrchestratorInfo) + } + return nil, err + } + defer completeBalanceUpdate(sess.BroadcastSession, balUpdate) + + start := time.Now() + resp, err := client.SegmentAnything2WithBodyWithResponse(ctx, mw.FormDataContentType(), &buf, setHeaders) + took := time.Since(start) + if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "segment anything 2", *req.ModelId, sess.OrchestratorInfo) + } + return nil, err + } + + if resp.JSON200 == nil { + // TODO: Replace trim newline with better error spec from O + return nil, errors.New(strings.TrimSuffix(string(resp.Body), "\n")) + } + + // We treat a response as "receiving change" where the change is the difference between the credit and debit for the update + if balUpdate != nil { + balUpdate.Status = ReceivedChange + } + + // TODO: Refine this rough estimate in future iterations + sess.LatencyScore = CalculateSegmentAnything2LatencyScore(took, outPixels) + + if monitor.Enabled { + var pricePerAIUnit float64 + if priceInfo := sess.OrchestratorInfo.GetPriceInfo(); priceInfo != nil && priceInfo.PixelsPerUnit != 0 { + pricePerAIUnit = float64(priceInfo.PricePerUnit) / float64(priceInfo.PixelsPerUnit) + } + + monitor.AIRequestFinished(ctx, "segment anything 2", *req.ModelId, monitor.AIJobInfo{LatencyScore: sess.LatencyScore, PricePerUnit: pricePerAIUnit}, sess.OrchestratorInfo) + } + + return resp.JSON200, nil +} + // CalculateAudioToTextLatencyScore computes the time taken per second of audio for an audio-to-text request. func CalculateAudioToTextLatencyScore(took time.Duration, durationSeconds int64) float64 { if durationSeconds <= 0 { @@ -744,6 +843,15 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface submitFn = func(ctx context.Context, params aiRequestParams, sess *AISession) (interface{}, error) { return submitAudioToText(ctx, params, sess, v) } + case worker.SegmentAnything2MultipartRequestBody: + cap = core.Capability_SegmentAnything2 + modelID = defaultSegmentAnything2ModelID + if v.ModelId != nil { + modelID = *v.ModelId + } + submitFn = func(ctx context.Context, params aiRequestParams, sess *AISession) (interface{}, error) { + return submitSegmentAnything2(ctx, params, sess, v) + } default: return nil, fmt.Errorf("unsupported request type %T", req) } diff --git a/server/rpc.go b/server/rpc.go index 0c8b9f806..db797b9eb 100644 --- a/server/rpc.go +++ b/server/rpc.go @@ -68,6 +68,7 @@ type Orchestrator interface { ImageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) Upscale(ctx context.Context, req worker.UpscaleMultipartRequestBody) (*worker.ImageResponse, error) AudioToText(ctx context.Context, req worker.AudioToTextMultipartRequestBody) (*worker.TextResponse, error) + SegmentAnything2(ctx context.Context, req worker.SegmentAnything2MultipartRequestBody) (*worker.MasksResponse, error) } // Balance describes methods for a session's balance maintenance diff --git a/server/rpc_test.go b/server/rpc_test.go index 0ede88b26..197e2baa6 100644 --- a/server/rpc_test.go +++ b/server/rpc_test.go @@ -202,6 +202,9 @@ func (r *stubOrchestrator) Upscale(ctx context.Context, req worker.UpscaleMultip func (r *stubOrchestrator) AudioToText(ctx context.Context, req worker.AudioToTextMultipartRequestBody) (*worker.TextResponse, error) { return nil, nil } +func (r *stubOrchestrator) SegmentAnything2(ctx context.Context, req worker.SegmentAnything2MultipartRequestBody) (*worker.MasksResponse, error) { + return nil, nil +} func (r *stubOrchestrator) CheckAICapacity(pipeline, modelID string) bool { return true } @@ -1385,6 +1388,9 @@ func (r *mockOrchestrator) Upscale(ctx context.Context, req worker.UpscaleMultip func (r *mockOrchestrator) AudioToText(ctx context.Context, req worker.AudioToTextMultipartRequestBody) (*worker.TextResponse, error) { return nil, nil } +func (r *mockOrchestrator) SegmentAnything2(ctx context.Context, req worker.SegmentAnything2MultipartRequestBody) (*worker.MasksResponse, error) { + return nil, nil +} func (r *mockOrchestrator) CheckAICapacity(pipeline, modelID string) bool { return true } From 1bae87ec854d875336fa42e1df1e885a54b49429 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Wed, 4 Sep 2024 12:41:25 +0200 Subject: [PATCH 199/203] chore(ai): release 0.7.8-ai.2 This commit released the new AI network software. --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 31f146361..6bf1c7735 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.7.8-ai.1 +0.7.8-ai.2 From c5f5a1177148ef78f933d46bd1fc2823a7665f91 Mon Sep 17 00:00:00 2001 From: ad-astra-video <99882368+ad-astra-video@users.noreply.github.com> Date: Wed, 11 Sep 2024 17:14:21 -0500 Subject: [PATCH 200/203] fix(ai): update SAM2 capability description to only upper case first letter (#3170) --- core/capabilities.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/capabilities.go b/core/capabilities.go index 734404ca8..fc9e5217b 100644 --- a/core/capabilities.go +++ b/core/capabilities.go @@ -115,7 +115,7 @@ var CapabilityNameLookup = map[Capability]string{ Capability_ImageToVideo: "Image to video", Capability_Upscale: "Upscale", Capability_AudioToText: "Audio to text", - Capability_SegmentAnything2: "Segment Anything 2", + Capability_SegmentAnything2: "Segment anything 2", } var CapabilityTestLookup = map[Capability]CapabilityTest{ From e01daa22d565a1652653ff802b861421661ac84e Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Fri, 13 Sep 2024 17:09:27 +0200 Subject: [PATCH 201/203] chore(ai): update ai-worker This commit updates the ai-worker to the one with the changed worker types. --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b0fd01bd9..6e5a284e6 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/golang/protobuf v1.5.4 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.2.0 + github.com/livepeer/ai-worker v0.3.0 github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240819180416-f87352959b85 diff --git a/go.sum b/go.sum index 3299db083..70fa5c13f 100644 --- a/go.sum +++ b/go.sum @@ -623,8 +623,8 @@ github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4n github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/livepeer/ai-worker v0.2.0 h1:u6m3nVQnisqWn2nMhgTgKLQby7VDAEp5xmeHt7Res/Y= -github.com/livepeer/ai-worker v0.2.0/go.mod h1:91lMzkzVuwR9kZ0EzXwf+7yVhLaNVmYAfmBtn7t3cQA= +github.com/livepeer/ai-worker v0.3.0 h1:fdxsHPO6/5CpTmqcyJoeWL4MCqxJVnLiDFCZYGCfxOs= +github.com/livepeer/ai-worker v0.3.0/go.mod h1:91lMzkzVuwR9kZ0EzXwf+7yVhLaNVmYAfmBtn7t3cQA= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= From 14f77830417eab9ade3394aa697e2a02c5b18263 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Fri, 13 Sep 2024 17:20:12 +0200 Subject: [PATCH 202/203] Revert "chore(ai): update ai-worker" This reverts commit e01daa22d565a1652653ff802b861421661ac84e. --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 6e5a284e6..b0fd01bd9 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/golang/protobuf v1.5.4 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.3.0 + github.com/livepeer/ai-worker v0.2.0 github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240819180416-f87352959b85 diff --git a/go.sum b/go.sum index 70fa5c13f..3299db083 100644 --- a/go.sum +++ b/go.sum @@ -623,8 +623,8 @@ github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4n github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/livepeer/ai-worker v0.3.0 h1:fdxsHPO6/5CpTmqcyJoeWL4MCqxJVnLiDFCZYGCfxOs= -github.com/livepeer/ai-worker v0.3.0/go.mod h1:91lMzkzVuwR9kZ0EzXwf+7yVhLaNVmYAfmBtn7t3cQA= +github.com/livepeer/ai-worker v0.2.0 h1:u6m3nVQnisqWn2nMhgTgKLQby7VDAEp5xmeHt7Res/Y= +github.com/livepeer/ai-worker v0.2.0/go.mod h1:91lMzkzVuwR9kZ0EzXwf+7yVhLaNVmYAfmBtn7t3cQA= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= From ffb1922d4a26a47b189c20e811de5fa67f9ebc66 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Fri, 13 Sep 2024 18:01:14 +0200 Subject: [PATCH 203/203] refactor: update worker classes (#3171) This commit ensures that the go-livepeer code uses the new worker classes that were defined in https://github.com/livepeer/ai-worker/pull/191. --- ai/file_worker.go | 8 +++--- core/ai.go | 12 ++++----- core/orchestrator.go | 24 ++++++++--------- go.mod | 2 +- go.sum | 4 +-- server/ai_http.go | 36 +++++++++++++------------- server/ai_mediaserver.go | 12 ++++----- server/ai_process.go | 56 ++++++++++++++++++++-------------------- server/rpc.go | 12 ++++----- server/rpc_test.go | 24 ++++++++--------- 10 files changed, 95 insertions(+), 95 deletions(-) diff --git a/ai/file_worker.go b/ai/file_worker.go index e9eb85c64..1ce547820 100644 --- a/ai/file_worker.go +++ b/ai/file_worker.go @@ -17,7 +17,7 @@ func NewFileWorker(files map[string]string) *FileWorker { return &FileWorker{files: files} } -func (w *FileWorker) TextToImage(ctx context.Context, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) { +func (w *FileWorker) TextToImage(ctx context.Context, req worker.GenTextToImageJSONRequestBody) (*worker.ImageResponse, error) { fname, ok := w.files["text-to-image"] if !ok { return nil, errors.New("text-to-image response file not found") @@ -36,7 +36,7 @@ func (w *FileWorker) TextToImage(ctx context.Context, req worker.TextToImageJSON return &resp, nil } -func (w *FileWorker) ImageToImage(ctx context.Context, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { +func (w *FileWorker) ImageToImage(ctx context.Context, req worker.GenImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { fname, ok := w.files["image-to-image"] if !ok { return nil, errors.New("image-to-image response file not found") @@ -55,7 +55,7 @@ func (w *FileWorker) ImageToImage(ctx context.Context, req worker.ImageToImageMu return &resp, nil } -func (w *FileWorker) ImageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) (*worker.VideoResponse, error) { +func (w *FileWorker) ImageToVideo(ctx context.Context, req worker.GenImageToVideoMultipartRequestBody) (*worker.VideoResponse, error) { fname, ok := w.files["image-to-video"] if !ok { return nil, errors.New("image-to-video response file not found") @@ -74,7 +74,7 @@ func (w *FileWorker) ImageToVideo(ctx context.Context, req worker.ImageToVideoMu return &resp, nil } -func (w *FileWorker) Upscale(ctx context.Context, req worker.UpscaleMultipartRequestBody) (*worker.ImageResponse, error) { +func (w *FileWorker) Upscale(ctx context.Context, req worker.GenUpscaleMultipartRequestBody) (*worker.ImageResponse, error) { fname, ok := w.files["upscale"] if !ok { return nil, errors.New("upscale response file not found") diff --git a/core/ai.go b/core/ai.go index 887e1f8c8..31f331e49 100644 --- a/core/ai.go +++ b/core/ai.go @@ -17,12 +17,12 @@ import ( var errPipelineNotAvailable = errors.New("pipeline not available") type AI interface { - TextToImage(context.Context, worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) - ImageToImage(context.Context, worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) - ImageToVideo(context.Context, worker.ImageToVideoMultipartRequestBody) (*worker.VideoResponse, error) - Upscale(context.Context, worker.UpscaleMultipartRequestBody) (*worker.ImageResponse, error) - AudioToText(context.Context, worker.AudioToTextMultipartRequestBody) (*worker.TextResponse, error) - SegmentAnything2(context.Context, worker.SegmentAnything2MultipartRequestBody) (*worker.MasksResponse, error) + TextToImage(context.Context, worker.GenTextToImageJSONRequestBody) (*worker.ImageResponse, error) + ImageToImage(context.Context, worker.GenImageToImageMultipartRequestBody) (*worker.ImageResponse, error) + ImageToVideo(context.Context, worker.GenImageToVideoMultipartRequestBody) (*worker.VideoResponse, error) + Upscale(context.Context, worker.GenUpscaleMultipartRequestBody) (*worker.ImageResponse, error) + AudioToText(context.Context, worker.GenAudioToTextMultipartRequestBody) (*worker.TextResponse, error) + SegmentAnything2(context.Context, worker.GenSegmentAnything2MultipartRequestBody) (*worker.MasksResponse, error) Warm(context.Context, string, string, worker.RunnerEndpoint, worker.OptimizationFlags) error Stop(context.Context) error HasCapacity(pipeline, modelID string) bool diff --git a/core/orchestrator.go b/core/orchestrator.go index ba9f470bd..f8e343ae3 100644 --- a/core/orchestrator.go +++ b/core/orchestrator.go @@ -110,27 +110,27 @@ func (orch *orchestrator) TranscoderResults(tcID int64, res *RemoteTranscoderRes orch.node.TranscoderManager.transcoderResults(tcID, res) } -func (orch *orchestrator) TextToImage(ctx context.Context, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) { +func (orch *orchestrator) TextToImage(ctx context.Context, req worker.GenTextToImageJSONRequestBody) (*worker.ImageResponse, error) { return orch.node.textToImage(ctx, req) } -func (orch *orchestrator) ImageToImage(ctx context.Context, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { +func (orch *orchestrator) ImageToImage(ctx context.Context, req worker.GenImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { return orch.node.imageToImage(ctx, req) } -func (orch *orchestrator) ImageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { +func (orch *orchestrator) ImageToVideo(ctx context.Context, req worker.GenImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { return orch.node.imageToVideo(ctx, req) } -func (orch *orchestrator) Upscale(ctx context.Context, req worker.UpscaleMultipartRequestBody) (*worker.ImageResponse, error) { +func (orch *orchestrator) Upscale(ctx context.Context, req worker.GenUpscaleMultipartRequestBody) (*worker.ImageResponse, error) { return orch.node.upscale(ctx, req) } -func (orch *orchestrator) AudioToText(ctx context.Context, req worker.AudioToTextMultipartRequestBody) (*worker.TextResponse, error) { +func (orch *orchestrator) AudioToText(ctx context.Context, req worker.GenAudioToTextMultipartRequestBody) (*worker.TextResponse, error) { return orch.node.AudioToText(ctx, req) } -func (orch *orchestrator) SegmentAnything2(ctx context.Context, req worker.SegmentAnything2MultipartRequestBody) (*worker.MasksResponse, error) { +func (orch *orchestrator) SegmentAnything2(ctx context.Context, req worker.GenSegmentAnything2MultipartRequestBody) (*worker.MasksResponse, error) { return orch.node.SegmentAnything2(ctx, req) } @@ -951,27 +951,27 @@ func (n *LivepeerNode) serveTranscoder(stream net.Transcoder_RegisterTranscoderS } } -func (n *LivepeerNode) textToImage(ctx context.Context, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) { +func (n *LivepeerNode) textToImage(ctx context.Context, req worker.GenTextToImageJSONRequestBody) (*worker.ImageResponse, error) { return n.AIWorker.TextToImage(ctx, req) } -func (n *LivepeerNode) imageToImage(ctx context.Context, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { +func (n *LivepeerNode) imageToImage(ctx context.Context, req worker.GenImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { return n.AIWorker.ImageToImage(ctx, req) } -func (n *LivepeerNode) upscale(ctx context.Context, req worker.UpscaleMultipartRequestBody) (*worker.ImageResponse, error) { +func (n *LivepeerNode) upscale(ctx context.Context, req worker.GenUpscaleMultipartRequestBody) (*worker.ImageResponse, error) { return n.AIWorker.Upscale(ctx, req) } -func (n *LivepeerNode) AudioToText(ctx context.Context, req worker.AudioToTextMultipartRequestBody) (*worker.TextResponse, error) { +func (n *LivepeerNode) AudioToText(ctx context.Context, req worker.GenAudioToTextMultipartRequestBody) (*worker.TextResponse, error) { return n.AIWorker.AudioToText(ctx, req) } -func (n *LivepeerNode) SegmentAnything2(ctx context.Context, req worker.SegmentAnything2MultipartRequestBody) (*worker.MasksResponse, error) { +func (n *LivepeerNode) SegmentAnything2(ctx context.Context, req worker.GenSegmentAnything2MultipartRequestBody) (*worker.MasksResponse, error) { return n.AIWorker.SegmentAnything2(ctx, req) } -func (n *LivepeerNode) imageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { +func (n *LivepeerNode) imageToVideo(ctx context.Context, req worker.GenImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { // We might support generating more than one video in the future (i.e. multiple input images/prompts) numVideos := 1 diff --git a/go.mod b/go.mod index b0fd01bd9..f84f4a6b3 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/golang/protobuf v1.5.4 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.2.0 + github.com/livepeer/ai-worker v0.5.0 github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b github.com/livepeer/livepeer-data v0.7.5-0.20231004073737-06f1f383fb18 github.com/livepeer/lpms v0.0.0-20240819180416-f87352959b85 diff --git a/go.sum b/go.sum index 3299db083..8c7b8cd8c 100644 --- a/go.sum +++ b/go.sum @@ -623,8 +623,8 @@ github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4n github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/livepeer/ai-worker v0.2.0 h1:u6m3nVQnisqWn2nMhgTgKLQby7VDAEp5xmeHt7Res/Y= -github.com/livepeer/ai-worker v0.2.0/go.mod h1:91lMzkzVuwR9kZ0EzXwf+7yVhLaNVmYAfmBtn7t3cQA= +github.com/livepeer/ai-worker v0.5.0 h1:dgO6j9QVFPOq9omIcgB1YmgVSlhV94BMb6QO4WUocX8= +github.com/livepeer/ai-worker v0.5.0/go.mod h1:91lMzkzVuwR9kZ0EzXwf+7yVhLaNVmYAfmBtn7t3cQA= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b h1:VQcnrqtCA2UROp7q8ljkh2XA/u0KRgVv0S1xoUvOweE= github.com/livepeer/go-tools v0.3.6-0.20240130205227-92479de8531b/go.mod h1:hwJ5DKhl+pTanFWl+EUpw1H7ukPO/H+MFpgA7jjshzw= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA= diff --git a/server/ai_http.go b/server/ai_http.go index a11164bc9..3f0bb97d9 100644 --- a/server/ai_http.go +++ b/server/ai_http.go @@ -56,7 +56,7 @@ func (h *lphttp) TextToImage() http.Handler { remoteAddr := getRemoteAddr(r) ctx := clog.AddVal(r.Context(), clog.ClientIP, remoteAddr) - var req worker.TextToImageJSONRequestBody + var req worker.GenTextToImageJSONRequestBody if err := json.NewDecoder(r.Body).Decode(&req); err != nil { respondWithError(w, err.Error(), http.StatusBadRequest) return @@ -79,7 +79,7 @@ func (h *lphttp) ImageToImage() http.Handler { return } - var req worker.ImageToImageMultipartRequestBody + var req worker.GenImageToImageMultipartRequestBody if err := runtime.BindMultipart(&req, *multiRdr); err != nil { respondWithError(w, err.Error(), http.StatusInternalServerError) return @@ -102,7 +102,7 @@ func (h *lphttp) ImageToVideo() http.Handler { return } - var req worker.ImageToVideoMultipartRequestBody + var req worker.GenImageToVideoMultipartRequestBody if err := runtime.BindMultipart(&req, *multiRdr); err != nil { respondWithError(w, err.Error(), http.StatusInternalServerError) return @@ -125,7 +125,7 @@ func (h *lphttp) Upscale() http.Handler { return } - var req worker.UpscaleMultipartRequestBody + var req worker.GenUpscaleMultipartRequestBody if err := runtime.BindMultipart(&req, *multiRdr); err != nil { respondWithError(w, err.Error(), http.StatusInternalServerError) return @@ -148,7 +148,7 @@ func (h *lphttp) AudioToText() http.Handler { return } - var req worker.AudioToTextMultipartRequestBody + var req worker.GenAudioToTextMultipartRequestBody if err := runtime.BindMultipart(&req, *multiRdr); err != nil { respondWithError(w, err.Error(), http.StatusInternalServerError) return @@ -171,7 +171,7 @@ func (h *lphttp) SegmentAnything2() http.Handler { return } - var req worker.SegmentAnything2MultipartRequestBody + var req worker.GenSegmentAnything2MultipartRequestBody if err := runtime.BindMultipart(&req, *multiRdr); err != nil { respondWithError(w, err.Error(), http.StatusInternalServerError) return @@ -202,7 +202,7 @@ func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request var outPixels int64 switch v := req.(type) { - case worker.TextToImageJSONRequestBody: + case worker.GenTextToImageJSONRequestBody: pipeline = "text-to-image" cap = core.Capability_TextToImage modelID = *v.ModelId @@ -226,7 +226,7 @@ func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request } outPixels = height * width * numImages - case worker.ImageToImageMultipartRequestBody: + case worker.GenImageToImageMultipartRequestBody: pipeline = "image-to-image" cap = core.Capability_ImageToImage modelID = *v.ModelId @@ -251,7 +251,7 @@ func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request } outPixels = int64(config.Height) * int64(config.Width) * numImages - case worker.UpscaleMultipartRequestBody: + case worker.GenUpscaleMultipartRequestBody: pipeline = "upscale" cap = core.Capability_Upscale modelID = *v.ModelId @@ -270,7 +270,7 @@ func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request return } outPixels = int64(config.Height) * int64(config.Width) - case worker.ImageToVideoMultipartRequestBody: + case worker.GenImageToVideoMultipartRequestBody: pipeline = "image-to-video" cap = core.Capability_ImageToVideo modelID = *v.ModelId @@ -291,7 +291,7 @@ func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request frames := int64(25) outPixels = height * width * int64(frames) - case worker.AudioToTextMultipartRequestBody: + case worker.GenAudioToTextMultipartRequestBody: pipeline = "audio-to-text" cap = core.Capability_AudioToText modelID = *v.ModelId @@ -305,7 +305,7 @@ func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request return } outPixels *= 1000 // Convert to milliseconds - case worker.SegmentAnything2MultipartRequestBody: + case worker.GenSegmentAnything2MultipartRequestBody: pipeline = "segment-anything-2" cap = core.Capability_SegmentAnything2 modelID = *v.ModelId @@ -382,20 +382,20 @@ func handleAIRequest(ctx context.Context, w http.ResponseWriter, r *http.Request if monitor.Enabled { var latencyScore float64 switch v := req.(type) { - case worker.TextToImageJSONRequestBody: + case worker.GenTextToImageJSONRequestBody: latencyScore = CalculateTextToImageLatencyScore(took, v, outPixels) - case worker.ImageToImageMultipartRequestBody: + case worker.GenImageToImageMultipartRequestBody: latencyScore = CalculateImageToImageLatencyScore(took, v, outPixels) - case worker.ImageToVideoMultipartRequestBody: + case worker.GenImageToVideoMultipartRequestBody: latencyScore = CalculateImageToVideoLatencyScore(took, v, outPixels) - case worker.UpscaleMultipartRequestBody: + case worker.GenUpscaleMultipartRequestBody: latencyScore = CalculateUpscaleLatencyScore(took, v, outPixels) - case worker.AudioToTextMultipartRequestBody: + case worker.GenAudioToTextMultipartRequestBody: durationSeconds, err := common.CalculateAudioDuration(v.Audio) if err == nil { latencyScore = CalculateAudioToTextLatencyScore(took, durationSeconds) } - case worker.SegmentAnything2MultipartRequestBody: + case worker.GenSegmentAnything2MultipartRequestBody: latencyScore = CalculateSegmentAnything2LatencyScore(took, outPixels) } diff --git a/server/ai_mediaserver.go b/server/ai_mediaserver.go index a600f3b17..078fa05ee 100644 --- a/server/ai_mediaserver.go +++ b/server/ai_mediaserver.go @@ -81,7 +81,7 @@ func (ls *LivepeerServer) TextToImage() http.Handler { requestID := string(core.RandomManifestID()) ctx = clog.AddVal(ctx, "request_id", requestID) - var req worker.TextToImageJSONRequestBody + var req worker.GenTextToImageJSONRequestBody if err := json.NewDecoder(r.Body).Decode(&req); err != nil { respondJsonError(ctx, w, err, http.StatusBadRequest) return @@ -129,7 +129,7 @@ func (ls *LivepeerServer) ImageToImage() http.Handler { return } - var req worker.ImageToImageMultipartRequestBody + var req worker.GenImageToImageMultipartRequestBody if err := runtime.BindMultipart(&req, *multiRdr); err != nil { respondJsonError(ctx, w, err, http.StatusBadRequest) return @@ -177,7 +177,7 @@ func (ls *LivepeerServer) ImageToVideo() http.Handler { return } - var req worker.ImageToVideoMultipartRequestBody + var req worker.GenImageToVideoMultipartRequestBody if err := runtime.BindMultipart(&req, *multiRdr); err != nil { respondJsonError(ctx, w, err, http.StatusBadRequest) return @@ -287,7 +287,7 @@ func (ls *LivepeerServer) Upscale() http.Handler { return } - var req worker.UpscaleMultipartRequestBody + var req worker.GenUpscaleMultipartRequestBody if err := runtime.BindMultipart(&req, *multiRdr); err != nil { respondJsonError(ctx, w, err, http.StatusBadRequest) return @@ -335,7 +335,7 @@ func (ls *LivepeerServer) AudioToText() http.Handler { return } - var req worker.AudioToTextMultipartRequestBody + var req worker.GenAudioToTextMultipartRequestBody if err := runtime.BindMultipart(&req, *multiRdr); err != nil { respondJsonError(ctx, w, err, http.StatusBadRequest) return @@ -388,7 +388,7 @@ func (ls *LivepeerServer) SegmentAnything2() http.Handler { return } - var req worker.SegmentAnything2MultipartRequestBody + var req worker.GenSegmentAnything2MultipartRequestBody if err := runtime.BindMultipart(&req, *multiRdr); err != nil { respondJsonError(ctx, w, err, http.StatusBadRequest) return diff --git a/server/ai_process.go b/server/ai_process.go index d69beb061..249cc506b 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -56,7 +56,7 @@ type aiRequestParams struct { } // CalculateTextToImageLatencyScore computes the time taken per pixel for an text-to-image request. -func CalculateTextToImageLatencyScore(took time.Duration, req worker.TextToImageJSONRequestBody, outPixels int64) float64 { +func CalculateTextToImageLatencyScore(took time.Duration, req worker.GenTextToImageJSONRequestBody, outPixels int64) float64 { if outPixels <= 0 { return 0 } @@ -75,7 +75,7 @@ func CalculateTextToImageLatencyScore(took time.Duration, req worker.TextToImage return took.Seconds() / float64(outPixels) / numInferenceSteps } -func processTextToImage(ctx context.Context, params aiRequestParams, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) { +func processTextToImage(ctx context.Context, params aiRequestParams, req worker.GenTextToImageJSONRequestBody) (*worker.ImageResponse, error) { resp, err := processAIRequest(ctx, params, req) if err != nil { return nil, err @@ -106,7 +106,7 @@ func processTextToImage(ctx context.Context, params aiRequestParams, req worker. return imgResp, nil } -func submitTextToImage(ctx context.Context, params aiRequestParams, sess *AISession, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) { +func submitTextToImage(ctx context.Context, params aiRequestParams, sess *AISession, req worker.GenTextToImageJSONRequestBody) (*worker.ImageResponse, error) { client, err := worker.NewClientWithResponses(sess.Transcoder(), worker.WithHTTPClient(httpClient)) if err != nil { @@ -146,7 +146,7 @@ func submitTextToImage(ctx context.Context, params aiRequestParams, sess *AISess defer completeBalanceUpdate(sess.BroadcastSession, balUpdate) start := time.Now() - resp, err := client.TextToImageWithResponse(ctx, req, setHeaders) + resp, err := client.GenTextToImageWithResponse(ctx, req, setHeaders) took := time.Since(start) // TODO: Refine this rough estimate in future iterations. @@ -182,7 +182,7 @@ func submitTextToImage(ctx context.Context, params aiRequestParams, sess *AISess } // CalculateImageToImageLatencyScore computes the time taken per pixel for an image-to-image request. -func CalculateImageToImageLatencyScore(took time.Duration, req worker.ImageToImageMultipartRequestBody, outPixels int64) float64 { +func CalculateImageToImageLatencyScore(took time.Duration, req worker.GenImageToImageMultipartRequestBody, outPixels int64) float64 { if outPixels <= 0 { return 0 } @@ -201,7 +201,7 @@ func CalculateImageToImageLatencyScore(took time.Duration, req worker.ImageToIma return took.Seconds() / float64(outPixels) / numInferenceSteps } -func processImageToImage(ctx context.Context, params aiRequestParams, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { +func processImageToImage(ctx context.Context, params aiRequestParams, req worker.GenImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { resp, err := processAIRequest(ctx, params, req) if err != nil { return nil, err @@ -232,7 +232,7 @@ func processImageToImage(ctx context.Context, params aiRequestParams, req worker return imgResp, nil } -func submitImageToImage(ctx context.Context, params aiRequestParams, sess *AISession, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { +func submitImageToImage(ctx context.Context, params aiRequestParams, sess *AISession, req worker.GenImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { // TODO: Default values for the number of images is currently hardcoded. // These should be managed by the nethttpmiddleware. Refer to issue LIV-412 for more details. defaultNumImages := 1 @@ -286,7 +286,7 @@ func submitImageToImage(ctx context.Context, params aiRequestParams, sess *AISes defer completeBalanceUpdate(sess.BroadcastSession, balUpdate) start := time.Now() - resp, err := client.ImageToImageWithBodyWithResponse(ctx, mw.FormDataContentType(), &buf, setHeaders) + resp, err := client.GenImageToImageWithBodyWithResponse(ctx, mw.FormDataContentType(), &buf, setHeaders) took := time.Since(start) // TODO: Refine this rough estimate in future iterations. @@ -322,7 +322,7 @@ func submitImageToImage(ctx context.Context, params aiRequestParams, sess *AISes } // CalculateImageToVideoLatencyScore computes the time taken per pixel for an image-to-video request. -func CalculateImageToVideoLatencyScore(took time.Duration, req worker.ImageToVideoMultipartRequestBody, outPixels int64) float64 { +func CalculateImageToVideoLatencyScore(took time.Duration, req worker.GenImageToVideoMultipartRequestBody, outPixels int64) float64 { if outPixels <= 0 { return 0 } @@ -337,7 +337,7 @@ func CalculateImageToVideoLatencyScore(took time.Duration, req worker.ImageToVid return took.Seconds() / float64(outPixels) / numInferenceSteps } -func processImageToVideo(ctx context.Context, params aiRequestParams, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { +func processImageToVideo(ctx context.Context, params aiRequestParams, req worker.GenImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { resp, err := processAIRequest(ctx, params, req) if err != nil { return nil, err @@ -373,7 +373,7 @@ func processImageToVideo(ctx context.Context, params aiRequestParams, req worker return imgResp, nil } -func submitImageToVideo(ctx context.Context, params aiRequestParams, sess *AISession, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { +func submitImageToVideo(ctx context.Context, params aiRequestParams, sess *AISession, req worker.GenImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { var buf bytes.Buffer mw, err := worker.NewImageToVideoMultipartWriter(&buf, req) if err != nil { @@ -412,7 +412,7 @@ func submitImageToVideo(ctx context.Context, params aiRequestParams, sess *AISes defer completeBalanceUpdate(sess.BroadcastSession, balUpdate) start := time.Now() - resp, err := client.ImageToVideoWithBody(ctx, mw.FormDataContentType(), &buf, setHeaders) + resp, err := client.GenImageToVideoWithBody(ctx, mw.FormDataContentType(), &buf, setHeaders) took := time.Since(start) if err != nil { if monitor.Enabled { @@ -463,7 +463,7 @@ func submitImageToVideo(ctx context.Context, params aiRequestParams, sess *AISes } // CalculateUpscaleLatencyScore computes the time taken per pixel for an upscale request. -func CalculateUpscaleLatencyScore(took time.Duration, req worker.UpscaleMultipartRequestBody, outPixels int64) float64 { +func CalculateUpscaleLatencyScore(took time.Duration, req worker.GenUpscaleMultipartRequestBody, outPixels int64) float64 { if outPixels <= 0 { return 0 } @@ -478,7 +478,7 @@ func CalculateUpscaleLatencyScore(took time.Duration, req worker.UpscaleMultipar return took.Seconds() / float64(outPixels) / numInferenceSteps } -func processUpscale(ctx context.Context, params aiRequestParams, req worker.UpscaleMultipartRequestBody) (*worker.ImageResponse, error) { +func processUpscale(ctx context.Context, params aiRequestParams, req worker.GenUpscaleMultipartRequestBody) (*worker.ImageResponse, error) { resp, err := processAIRequest(ctx, params, req) if err != nil { return nil, err @@ -509,7 +509,7 @@ func processUpscale(ctx context.Context, params aiRequestParams, req worker.Upsc return imgResp, nil } -func submitUpscale(ctx context.Context, params aiRequestParams, sess *AISession, req worker.UpscaleMultipartRequestBody) (*worker.ImageResponse, error) { +func submitUpscale(ctx context.Context, params aiRequestParams, sess *AISession, req worker.GenUpscaleMultipartRequestBody) (*worker.ImageResponse, error) { var buf bytes.Buffer mw, err := worker.NewUpscaleMultipartWriter(&buf, req) if err != nil { @@ -553,7 +553,7 @@ func submitUpscale(ctx context.Context, params aiRequestParams, sess *AISession, defer completeBalanceUpdate(sess.BroadcastSession, balUpdate) start := time.Now() - resp, err := client.UpscaleWithBodyWithResponse(ctx, mw.FormDataContentType(), &buf, setHeaders) + resp, err := client.GenUpscaleWithBodyWithResponse(ctx, mw.FormDataContentType(), &buf, setHeaders) took := time.Since(start) if err != nil { if monitor.Enabled { @@ -596,7 +596,7 @@ func CalculateSegmentAnything2LatencyScore(took time.Duration, outPixels int64) return took.Seconds() / float64(outPixels) } -func processSegmentAnything2(ctx context.Context, params aiRequestParams, req worker.SegmentAnything2MultipartRequestBody) (*worker.MasksResponse, error) { +func processSegmentAnything2(ctx context.Context, params aiRequestParams, req worker.GenSegmentAnything2MultipartRequestBody) (*worker.MasksResponse, error) { resp, err := processAIRequest(ctx, params, req) if err != nil { return nil, err @@ -607,7 +607,7 @@ func processSegmentAnything2(ctx context.Context, params aiRequestParams, req wo return txtResp, nil } -func submitSegmentAnything2(ctx context.Context, params aiRequestParams, sess *AISession, req worker.BodySegmentAnything2SegmentAnything2Post) (*worker.MasksResponse, error) { +func submitSegmentAnything2(ctx context.Context, params aiRequestParams, sess *AISession, req worker.GenSegmentAnything2MultipartRequestBody) (*worker.MasksResponse, error) { var buf bytes.Buffer mw, err := worker.NewSegmentAnything2MultipartWriter(&buf, req) if err != nil { @@ -651,7 +651,7 @@ func submitSegmentAnything2(ctx context.Context, params aiRequestParams, sess *A defer completeBalanceUpdate(sess.BroadcastSession, balUpdate) start := time.Now() - resp, err := client.SegmentAnything2WithBodyWithResponse(ctx, mw.FormDataContentType(), &buf, setHeaders) + resp, err := client.GenSegmentAnything2WithBodyWithResponse(ctx, mw.FormDataContentType(), &buf, setHeaders) took := time.Since(start) if err != nil { if monitor.Enabled { @@ -694,7 +694,7 @@ func CalculateAudioToTextLatencyScore(took time.Duration, durationSeconds int64) return took.Seconds() / float64(durationSeconds) } -func processAudioToText(ctx context.Context, params aiRequestParams, req worker.AudioToTextMultipartRequestBody) (*worker.TextResponse, error) { +func processAudioToText(ctx context.Context, params aiRequestParams, req worker.GenAudioToTextMultipartRequestBody) (*worker.TextResponse, error) { resp, err := processAIRequest(ctx, params, req) if err != nil { return nil, err @@ -705,7 +705,7 @@ func processAudioToText(ctx context.Context, params aiRequestParams, req worker. return txtResp, nil } -func submitAudioToText(ctx context.Context, params aiRequestParams, sess *AISession, req worker.AudioToTextMultipartRequestBody) (*worker.TextResponse, error) { +func submitAudioToText(ctx context.Context, params aiRequestParams, sess *AISession, req worker.GenAudioToTextMultipartRequestBody) (*worker.TextResponse, error) { var buf bytes.Buffer mw, err := worker.NewAudioToTextMultipartWriter(&buf, req) if err != nil { @@ -742,7 +742,7 @@ func submitAudioToText(ctx context.Context, params aiRequestParams, sess *AISess defer completeBalanceUpdate(sess.BroadcastSession, balUpdate) start := time.Now() - resp, err := client.AudioToTextWithBody(ctx, mw.FormDataContentType(), &buf, setHeaders) + resp, err := client.GenAudioToTextWithBody(ctx, mw.FormDataContentType(), &buf, setHeaders) took := time.Since(start) if err != nil { if monitor.Enabled { @@ -798,7 +798,7 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface var submitFn func(context.Context, aiRequestParams, *AISession) (interface{}, error) switch v := req.(type) { - case worker.TextToImageJSONRequestBody: + case worker.GenTextToImageJSONRequestBody: cap = core.Capability_TextToImage modelID = defaultTextToImageModelID if v.ModelId != nil { @@ -807,7 +807,7 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface submitFn = func(ctx context.Context, params aiRequestParams, sess *AISession) (interface{}, error) { return submitTextToImage(ctx, params, sess, v) } - case worker.ImageToImageMultipartRequestBody: + case worker.GenImageToImageMultipartRequestBody: cap = core.Capability_ImageToImage modelID = defaultImageToImageModelID if v.ModelId != nil { @@ -816,7 +816,7 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface submitFn = func(ctx context.Context, params aiRequestParams, sess *AISession) (interface{}, error) { return submitImageToImage(ctx, params, sess, v) } - case worker.ImageToVideoMultipartRequestBody: + case worker.GenImageToVideoMultipartRequestBody: cap = core.Capability_ImageToVideo modelID = defaultImageToVideoModelID if v.ModelId != nil { @@ -825,7 +825,7 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface submitFn = func(ctx context.Context, params aiRequestParams, sess *AISession) (interface{}, error) { return submitImageToVideo(ctx, params, sess, v) } - case worker.UpscaleMultipartRequestBody: + case worker.GenUpscaleMultipartRequestBody: cap = core.Capability_Upscale modelID = defaultUpscaleModelID if v.ModelId != nil { @@ -834,7 +834,7 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface submitFn = func(ctx context.Context, params aiRequestParams, sess *AISession) (interface{}, error) { return submitUpscale(ctx, params, sess, v) } - case worker.AudioToTextMultipartRequestBody: + case worker.GenAudioToTextMultipartRequestBody: cap = core.Capability_AudioToText modelID = defaultAudioToTextModelID if v.ModelId != nil { @@ -843,7 +843,7 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface submitFn = func(ctx context.Context, params aiRequestParams, sess *AISession) (interface{}, error) { return submitAudioToText(ctx, params, sess, v) } - case worker.SegmentAnything2MultipartRequestBody: + case worker.GenSegmentAnything2MultipartRequestBody: cap = core.Capability_SegmentAnything2 modelID = defaultSegmentAnything2ModelID if v.ModelId != nil { diff --git a/server/rpc.go b/server/rpc.go index db797b9eb..6c1365ccd 100644 --- a/server/rpc.go +++ b/server/rpc.go @@ -63,12 +63,12 @@ type Orchestrator interface { DebitFees(addr ethcommon.Address, manifestID core.ManifestID, price *net.PriceInfo, pixels int64) Capabilities() *net.Capabilities AuthToken(sessionID string, expiration int64) *net.AuthToken - TextToImage(ctx context.Context, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) - ImageToImage(ctx context.Context, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) - ImageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) - Upscale(ctx context.Context, req worker.UpscaleMultipartRequestBody) (*worker.ImageResponse, error) - AudioToText(ctx context.Context, req worker.AudioToTextMultipartRequestBody) (*worker.TextResponse, error) - SegmentAnything2(ctx context.Context, req worker.SegmentAnything2MultipartRequestBody) (*worker.MasksResponse, error) + TextToImage(ctx context.Context, req worker.GenTextToImageJSONRequestBody) (*worker.ImageResponse, error) + ImageToImage(ctx context.Context, req worker.GenImageToImageMultipartRequestBody) (*worker.ImageResponse, error) + ImageToVideo(ctx context.Context, req worker.GenImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) + Upscale(ctx context.Context, req worker.GenUpscaleMultipartRequestBody) (*worker.ImageResponse, error) + AudioToText(ctx context.Context, req worker.GenAudioToTextMultipartRequestBody) (*worker.TextResponse, error) + SegmentAnything2(ctx context.Context, req worker.GenSegmentAnything2MultipartRequestBody) (*worker.MasksResponse, error) } // Balance describes methods for a session's balance maintenance diff --git a/server/rpc_test.go b/server/rpc_test.go index 197e2baa6..6f710a319 100644 --- a/server/rpc_test.go +++ b/server/rpc_test.go @@ -187,22 +187,22 @@ func (r *stubOrchestrator) TranscoderSecret() string { func (r *stubOrchestrator) PriceInfoForCaps(sender ethcommon.Address, manifestID core.ManifestID, caps *net.Capabilities) (*net.PriceInfo, error) { return &net.PriceInfo{PricePerUnit: 4, PixelsPerUnit: 1}, nil } -func (r *stubOrchestrator) TextToImage(ctx context.Context, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) { +func (r *stubOrchestrator) TextToImage(ctx context.Context, req worker.GenTextToImageJSONRequestBody) (*worker.ImageResponse, error) { return nil, nil } -func (r *stubOrchestrator) ImageToImage(ctx context.Context, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { +func (r *stubOrchestrator) ImageToImage(ctx context.Context, req worker.GenImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { return nil, nil } -func (r *stubOrchestrator) ImageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { +func (r *stubOrchestrator) ImageToVideo(ctx context.Context, req worker.GenImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { return nil, nil } -func (r *stubOrchestrator) Upscale(ctx context.Context, req worker.UpscaleMultipartRequestBody) (*worker.ImageResponse, error) { +func (r *stubOrchestrator) Upscale(ctx context.Context, req worker.GenUpscaleMultipartRequestBody) (*worker.ImageResponse, error) { return nil, nil } -func (r *stubOrchestrator) AudioToText(ctx context.Context, req worker.AudioToTextMultipartRequestBody) (*worker.TextResponse, error) { +func (r *stubOrchestrator) AudioToText(ctx context.Context, req worker.GenAudioToTextMultipartRequestBody) (*worker.TextResponse, error) { return nil, nil } -func (r *stubOrchestrator) SegmentAnything2(ctx context.Context, req worker.SegmentAnything2MultipartRequestBody) (*worker.MasksResponse, error) { +func (r *stubOrchestrator) SegmentAnything2(ctx context.Context, req worker.GenSegmentAnything2MultipartRequestBody) (*worker.MasksResponse, error) { return nil, nil } func (r *stubOrchestrator) CheckAICapacity(pipeline, modelID string) bool { @@ -1373,22 +1373,22 @@ func (o *mockOrchestrator) AuthToken(sessionID string, expiration int64) *net.Au func (r *mockOrchestrator) PriceInfoForCaps(sender ethcommon.Address, manifestID core.ManifestID, caps *net.Capabilities) (*net.PriceInfo, error) { return &net.PriceInfo{PricePerUnit: 4, PixelsPerUnit: 1}, nil } -func (r *mockOrchestrator) TextToImage(ctx context.Context, req worker.TextToImageJSONRequestBody) (*worker.ImageResponse, error) { +func (r *mockOrchestrator) TextToImage(ctx context.Context, req worker.GenTextToImageJSONRequestBody) (*worker.ImageResponse, error) { return nil, nil } -func (r *mockOrchestrator) ImageToImage(ctx context.Context, req worker.ImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { +func (r *mockOrchestrator) ImageToImage(ctx context.Context, req worker.GenImageToImageMultipartRequestBody) (*worker.ImageResponse, error) { return nil, nil } -func (r *mockOrchestrator) ImageToVideo(ctx context.Context, req worker.ImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { +func (r *mockOrchestrator) ImageToVideo(ctx context.Context, req worker.GenImageToVideoMultipartRequestBody) (*worker.ImageResponse, error) { return nil, nil } -func (r *mockOrchestrator) Upscale(ctx context.Context, req worker.UpscaleMultipartRequestBody) (*worker.ImageResponse, error) { +func (r *mockOrchestrator) Upscale(ctx context.Context, req worker.GenUpscaleMultipartRequestBody) (*worker.ImageResponse, error) { return nil, nil } -func (r *mockOrchestrator) AudioToText(ctx context.Context, req worker.AudioToTextMultipartRequestBody) (*worker.TextResponse, error) { +func (r *mockOrchestrator) AudioToText(ctx context.Context, req worker.GenAudioToTextMultipartRequestBody) (*worker.TextResponse, error) { return nil, nil } -func (r *mockOrchestrator) SegmentAnything2(ctx context.Context, req worker.SegmentAnything2MultipartRequestBody) (*worker.MasksResponse, error) { +func (r *mockOrchestrator) SegmentAnything2(ctx context.Context, req worker.GenSegmentAnything2MultipartRequestBody) (*worker.MasksResponse, error) { return nil, nil } func (r *mockOrchestrator) CheckAICapacity(pipeline, modelID string) bool {