diff --git a/.gitignore b/.gitignore index 9d493d245f..20d2d987ca 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ # IDE files *.vscode *.code-workspace +.aider* # Test binary, build with `go test -c` *.test diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fad51e72d..0c07c52246 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,35 @@ # Changelog -## V0.8.3 +## v0.8.4 ### Features ⚒ #### General -- [#3365](https://github.com/livepeer/go-livepeer/pull/3336/) updated AI llm pipeline to new OpenAI compatible API format. +- [#3411](https://github.com/livepeer/go-livepeer/pull/3411) Increase ticket params expiration from 10 (~2 min) to 40 (~8 min) (@leszko) +- A number of changes related to the Realtime Video AI (Live Video to Video workflow) + +#### Broadcaster +- [#3393](https://github.com/livepeer/go-livepeer/pull/3393) Improve AI Session Selection (@ad-astra-video) +- [#3402](https://github.com/livepeer/go-livepeer/pull/3402) Add Min Initial Latency Selector (@leszko) + +#### Orchestrator + +- [#3345](https://github.com/livepeer/go-livepeer/pull/3345) Move `ai-worker` code to a local package +- [#3399](https://github.com/livepeer/go-livepeer/pull/3399) Restart warm containers when they crash (@victorges) + +### Bug Fixes 🐞 + +#### CLI + +- [#3275](https://github.com/livepeer/go-livepeer/pull/3275) - Provide AI orchestrators with a way to vote on active proposal through the CLI. + +## V0.8.3 + +### Features ⚒ + +#### General +- [#3365](https://github.com/livepeer/go-livepeer/pull/3336/) updated AI llm pipeline to new OpenAI compatible API format. #### Orchestrator @@ -353,7 +376,7 @@ None #### Broadcaster -- [#2666](https://github.com/livepeer/go-livepeer/pull/2666) Re-use a session as long as it passes the latency score threshold check (@yondonfu) +- [#2666](https://github.com/livepeer/go-livepeer/pull/2666) Reuse a session as long as it passes the latency score threshold check (@yondonfu) #### Orchestrator - [#2639](https://github.com/livepeer/go-livepeer/pull/2639) Increase IdleTimeout for HTTP connections (@leszko) @@ -963,7 +986,7 @@ Thanks to everyone that submitted bug reports and assisted in testing! - [#1845](https://github.com/livepeer/go-livepeer/pull/1845) Staking actions with hints (@kyriediculous) - [#1873](https://github.com/livepeer/go-livepeer/pull/1873) Increase TicketParams expiration to 10 blocks (@kyriediculous) -- [#1849](https://github.com/livepeer/go-livepeer/pull/1849) Re-use remote transcoders for a stream sessions (@reubenr0d) +- [#1849](https://github.com/livepeer/go-livepeer/pull/1849) Reuse remote transcoders for a stream sessions (@reubenr0d) #### Transcoder @@ -1098,4 +1121,4 @@ Thanks everyone that submitted bug reports and assisted in testing! - [#1775](https://github.com/livepeer/go-livepeer/pull/1775) Fix transcoder load balancer race condition around session cleanup (@jailuthra) - [#1784](https://github.com/livepeer/go-livepeer/pull/1784) Use auth token sessionID to index into sessions map in transcoder load balancer (@jailuthra) -[Full list of changes](https://github.com/livepeer/go-livepeer/compare/v0.5.14...v0.5.15) +[Full list of changes](https://github.com/livepeer/go-livepeer/compare/v0.5.14...v0.5.15) \ No newline at end of file diff --git a/Makefile b/Makefile index 10b60762c4..60be49ad04 100644 --- a/Makefile +++ b/Makefile @@ -95,7 +95,17 @@ ifeq ($(BUILDOS),linux) endif -.PHONY: livepeer livepeer_bench livepeer_cli livepeer_router docker swagger +.PHONY: ai_worker_codegen livepeer livepeer_bench livepeer_cli livepeer_router docker swagger + +# Git reference to download the OpenAPI spec from, defaults to `main` branch. +# It can also be a simple git commit hash. e.g. `make ai_worker_codegen REF=c19289d` +REF ?= refs/heads/main +ai_worker_codegen: + go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen@v2.2.0 \ + -package worker \ + -generate types,client,chi-server,spec \ + https://raw.githubusercontent.com/livepeer/ai-worker/$(REF)/runner/openapi.yaml \ + | awk '!/WARNING/' > ai/worker/runner.gen.go livepeer: GO111MODULE=on CGO_ENABLED=1 CC="$(cc)" CGO_CFLAGS="$(cgo_cflags)" CGO_LDFLAGS="$(cgo_ldflags) ${CGO_LDFLAGS}" go build -o $(GO_BUILD_DIR) -tags "$(BUILD_TAGS)" -ldflags="$(ldflags)" cmd/livepeer/*.go @@ -116,4 +126,4 @@ docker_mtx: docker buildx build -f docker/Dockerfile.mediamtx docker/ swagger: - swag init --generalInfo cmd/livepeer/livepeer.go --outputTypes yaml --output . && mv swagger.yaml openapi.yaml + swag init --generalInfo server/ai_mediaserver.go --outputTypes yaml --output . && mv swagger.yaml liveai.openapi.yaml diff --git a/README.md b/README.md index ce714dd987..701dd8e12f 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ --- [![Go Report Card](https://goreportcard.com/badge/github.com/livepeer/go-livepeer)](https://goreportcard.com/report/github.com/livepeer/go-livepeer) -[![Discord](https://img.shields.io/discord/423160867534929930.svg?style=flat-square)](https://discord.gg/7wRSUGX) +[![Discord](https://img.shields.io/discord/423160867534929930.svg?style=flat-square)](https://discord.gg/livepeer) [![license](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE) [![Contributions welcome](https://img.shields.io/badge/contributions-welcome-orange.svg?style=flat-square)](CONTRIBUTING.md) @@ -21,10 +21,10 @@ serve as the live media layer in the decentralized development (Web3) stack. ## Table of Contents +- [Table of Contents](#table-of-contents) - [Requirements](#requirements) - [Getting Started](#getting-started) - [Contributing](#contributing) -- [Other Documentation](#documentation) - [Resources](#resources) @@ -63,5 +63,5 @@ other resources: - 📖 [The Livepeer Docs](https://livepeer.org/docs) - 🔭 [The 10-Minute Primer](https://livepeer.org/primer/) - ✍ [The Livepeer Blog](https://medium.com/livepeer-blog) -- 💬 [The Livepeer Chat](https://discord.gg/uaPhtyrWsF) +- 💬 [The Livepeer Chat](https://discord.gg/livepeer) - ❓ [The Livepeer Forum](https://forum.livepeer.org/) diff --git a/VERSION b/VERSION index ee94dd834b..fcbb5375b7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.8.3 +0.8.4 \ No newline at end of file diff --git a/ai/file_worker.go b/ai/file_worker.go index 1ce5478204..f37e9aec04 100644 --- a/ai/file_worker.go +++ b/ai/file_worker.go @@ -6,7 +6,7 @@ import ( "errors" "os" - "github.com/livepeer/ai-worker/worker" + "github.com/livepeer/go-livepeer/ai/worker" ) type FileWorker struct { diff --git a/ai/worker/b64.go b/ai/worker/b64.go new file mode 100644 index 0000000000..556b7a26d9 --- /dev/null +++ b/ai/worker/b64.go @@ -0,0 +1,61 @@ +package worker + +import ( + "bytes" + "fmt" + "image" + "image/gif" + "image/jpeg" + "image/png" + "io" + "os" + + "github.com/vincent-petithory/dataurl" +) + +func ReadImageB64DataUrl(url string, w io.Writer) error { + dataURL, err := dataurl.DecodeString(url) + if err != nil { + return err + } + + img, _, err := image.Decode(bytes.NewReader(dataURL.Data)) + if err != nil { + return err + } + + switch dataURL.MediaType.ContentType() { + case "image/png": + err = png.Encode(w, img) + case "image/jpg", "image/jpeg": + err = jpeg.Encode(w, img, nil) + case "image/gif": + err = gif.Encode(w, img, nil) + // Add cases for other image formats if necessary + default: + return fmt.Errorf("unsupported image format: %s", dataURL.MediaType.ContentType()) + } + + return err +} + +func SaveImageB64DataUrl(url, outputPath string) error { + file, err := os.Create(outputPath) + if err != nil { + return err + } + defer file.Close() + + return ReadImageB64DataUrl(url, file) +} + +func ReadAudioB64DataUrl(url string, w io.Writer) error { + dataURL, err := dataurl.DecodeString(url) + if err != nil { + return err + } + + w.Write(dataURL.Data) + + return nil +} diff --git a/ai/worker/b64_test.go b/ai/worker/b64_test.go new file mode 100644 index 0000000000..443329637d --- /dev/null +++ b/ai/worker/b64_test.go @@ -0,0 +1,93 @@ +package worker + +import ( + "bytes" + "encoding/base64" + "image" + "image/color" + "image/png" + "os" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestReadImageB64DataUrl(t *testing.T) { + tests := []struct { + name string + dataURL string + expectError bool + }{ + { + name: "Valid PNG Image", + dataURL: func() string { + img := image.NewRGBA(image.Rect(0, 0, 1, 1)) + img.Set(0, 0, color.RGBA{255, 0, 0, 255}) // Set a single red pixel + var imgBuf bytes.Buffer + err := png.Encode(&imgBuf, img) + require.NoError(t, err) + + return "data:image/png;base64," + base64.StdEncoding.EncodeToString(imgBuf.Bytes()) + }(), + expectError: false, + }, + { + name: "Unsupported Image Format", + dataURL: "data:image/bmp;base64," + base64.StdEncoding.EncodeToString([]byte{ + 0x42, 0x4D, // BMP header + // ... (rest of the BMP data) + }), + expectError: true, + }, + { + name: "Invalid Data URL", + dataURL: "invalid-data-url", + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var buf bytes.Buffer + err := ReadImageB64DataUrl(tt.dataURL, &buf) + if tt.expectError { + require.Error(t, err) + } else { + require.NoError(t, err) + require.NotEmpty(t, buf.Bytes()) + } + }) + } +} + +func TestSaveImageB64DataUrl(t *testing.T) { + img := image.NewRGBA(image.Rect(0, 0, 1, 1)) + img.Set(0, 0, color.RGBA{255, 0, 0, 255}) // Set a single red pixel + var imgBuf bytes.Buffer + err := png.Encode(&imgBuf, img) + require.NoError(t, err) + dataURL := "data:image/png;base64," + base64.StdEncoding.EncodeToString(imgBuf.Bytes()) + + outputPath := "test_output.png" + defer os.Remove(outputPath) + + err = SaveImageB64DataUrl(dataURL, outputPath) + require.NoError(t, err) + + // Verify that the file was created and is not empty + fileInfo, err := os.Stat(outputPath) + require.NoError(t, err) + require.False(t, fileInfo.IsDir()) + require.NotZero(t, fileInfo.Size()) +} + +func TestReadAudioB64DataUrl(t *testing.T) { + // Create a sample audio data and encode it as a data URL + audioData := []byte{0x00, 0x01, 0x02, 0x03, 0x04} + dataURL := "data:audio/wav;base64," + base64.StdEncoding.EncodeToString(audioData) + + var buf bytes.Buffer + err := ReadAudioB64DataUrl(dataURL, &buf) + require.NoError(t, err) + require.Equal(t, audioData, buf.Bytes()) +} diff --git a/ai/worker/container.go b/ai/worker/container.go new file mode 100644 index 0000000000..ee58072b5a --- /dev/null +++ b/ai/worker/container.go @@ -0,0 +1,126 @@ +package worker + +import ( + "context" + "errors" + "log/slog" + "sync" + "time" + + "github.com/deepmap/oapi-codegen/v2/pkg/securityprovider" +) + +type RunnerContainerType int + +const ( + Managed RunnerContainerType = iota + External +) + +type RunnerContainer struct { + RunnerContainerConfig + Name string + Client *ClientWithResponses + Hardware *HardwareInformation + + BorrowCtx context.Context + sync.RWMutex +} + +type RunnerEndpoint struct { + URL string + Token string +} + +type RunnerContainerConfig struct { + Type RunnerContainerType + Pipeline string + ModelID string + Endpoint RunnerEndpoint + ContainerImageID string + + // For managed containers only + ID string + GPU string + KeepWarm bool + OptimizationFlags OptimizationFlags + containerTimeout time.Duration +} + +// Create global references to functions to allow for mocking in tests. +var runnerWaitUntilReadyFunc = runnerWaitUntilReady + +func NewRunnerContainer(ctx context.Context, cfg RunnerContainerConfig, name string) (*RunnerContainer, error) { + // Ensure that timeout is set to a non-zero value. + timeout := cfg.containerTimeout + if timeout == 0 { + timeout = containerTimeout + } + + var opts []ClientOption + if cfg.Endpoint.Token != "" { + bearerTokenProvider, err := securityprovider.NewSecurityProviderBearerToken(cfg.Endpoint.Token) + if err != nil { + return nil, err + } + + opts = append(opts, WithRequestEditorFn(bearerTokenProvider.Intercept)) + } + + client, err := NewClientWithResponses(cfg.Endpoint.URL, opts...) + if err != nil { + return nil, err + } + + cctx, cancel := context.WithTimeout(ctx, cfg.containerTimeout) + defer cancel() + if err := runnerWaitUntilReadyFunc(cctx, client, pollingInterval); err != nil { + return nil, err + } + + var hardware *HardwareInformation + hctx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + hdw, err := getRunnerHardware(hctx, client) + if err != nil { + hardware = &HardwareInformation{Pipeline: cfg.Pipeline, ModelId: cfg.ModelID, GpuInfo: nil} + } else { + hardware = hdw + } + + return &RunnerContainer{ + RunnerContainerConfig: cfg, + Name: name, + Client: client, + Hardware: hardware, + }, nil +} + +func runnerWaitUntilReady(ctx context.Context, client *ClientWithResponses, pollingInterval time.Duration) error { + ticker := time.NewTicker(pollingInterval) + defer ticker.Stop() + +tickerLoop: + for range ticker.C { + select { + case <-ctx.Done(): + return errors.New("timed out waiting for runner") + default: + if _, err := client.HealthWithResponse(ctx); err == nil { + break tickerLoop + } + } + } + + return nil +} + +func getRunnerHardware(ctx context.Context, client *ClientWithResponses) (*HardwareInformation, error) { + resp, err := client.HardwareInfoWithResponse(ctx) + if err != nil { + slog.Error("Error getting hardware info for runner", slog.String("error", err.Error())) + return nil, err + } + + return resp.JSON200, nil +} diff --git a/ai/worker/doc.go b/ai/worker/doc.go new file mode 100644 index 0000000000..dbb7dff75a --- /dev/null +++ b/ai/worker/doc.go @@ -0,0 +1,20 @@ +/* +Package `worker` hosts the main AI worker logic for managing or using runner +containers for processing inference requests on the Livepeer AI subnet. The +package allows interacting with the [AI runner containers], and it includes: + +- Golang API Bindings (./runner.gen.go): + +Generated from the AI runner's OpenAPI spec. To re-generate them run: `make ai_worker_codegen` + +- Worker (./worker.go): + +Listens for inference requests from the Livepeer AI subnet and routes them to the AI runner. + +- Docker Manager (./docker.go): + +Manages AI runner containers. For a state diagram showing the lifecycle of a container, see the /doc/worker.md file. + +[AI runner containers]: https://github.com/livepeer/ai-runner +*/ +package worker diff --git a/ai/worker/docker.go b/ai/worker/docker.go new file mode 100644 index 0000000000..6ec823c3b1 --- /dev/null +++ b/ai/worker/docker.go @@ -0,0 +1,684 @@ +package worker + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "log/slog" + "runtime/debug" + "strconv" + "strings" + "sync" + "time" + + "github.com/docker/cli/opts" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/image" + "github.com/docker/docker/api/types/mount" + "github.com/docker/docker/api/types/network" + docker "github.com/docker/docker/client" + "github.com/docker/docker/errdefs" + "github.com/docker/docker/pkg/jsonmessage" + "github.com/docker/go-connections/nat" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" +) + +const containerModelDir = "/models" +const containerPort = "8000/tcp" +const pollingInterval = 500 * time.Millisecond +const containerTimeout = 2 * time.Minute +const externalContainerTimeout = 2 * time.Minute +const optFlagsContainerTimeout = 5 * time.Minute +const containerRemoveTimeout = 30 * time.Second +const containerCreatorLabel = "creator" +const containerCreator = "ai-worker" + +var containerWatchInterval = 5 * time.Second +var pipelineStartGracePeriod = 60 * time.Second +var maxHealthCheckFailures = 2 + +// This only works right now on a single GPU because if there is another container +// using the GPU we stop it so we don't have to worry about having enough ports +var containerHostPorts = map[string]string{ + "text-to-image": "8000", + "image-to-image": "8100", + "image-to-video": "8200", + "upscale": "8300", + "audio-to-text": "8400", + "llm": "8500", + "segment-anything-2": "8600", + "image-to-text": "8700", + "text-to-speech": "8800", + "live-video-to-video": "8900", +} + +// Default pipeline container image mapping to use if no overrides are provided. +var defaultBaseImage = "livepeer/ai-runner:latest" +var pipelineToImage = map[string]string{ + "segment-anything-2": "livepeer/ai-runner:segment-anything-2", + "text-to-speech": "livepeer/ai-runner:text-to-speech", + "audio-to-text": "livepeer/ai-runner:audio-to-text", + "llm": "livepeer/ai-runner:llm", +} +var livePipelineToImage = map[string]string{ + "streamdiffusion": "livepeer/ai-runner:live-app-streamdiffusion", + "comfyui": "livepeer/ai-runner:live-app-comfyui", + "segment_anything_2": "livepeer/ai-runner:live-app-segment_anything_2", + "noop": "livepeer/ai-runner:live-app-noop", +} + +type ImageOverrides struct { + Default string `json:"default"` + Batch map[string]string `json:"batch"` + Live map[string]string `json:"live"` +} + +// DockerClient is an interface for the Docker client, allowing for mocking in tests. +// NOTE: ensure any docker.Client methods used in this package are added. +type DockerClient interface { + ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error) + ContainerInspect(ctx context.Context, containerID string) (types.ContainerJSON, error) + ContainerList(ctx context.Context, options container.ListOptions) ([]types.Container, error) + ContainerRemove(ctx context.Context, containerID string, options container.RemoveOptions) error + ContainerStart(ctx context.Context, containerID string, options container.StartOptions) error + ContainerStop(ctx context.Context, containerID string, options container.StopOptions) error + ImageInspectWithRaw(ctx context.Context, imageID string) (types.ImageInspect, []byte, error) + ImagePull(ctx context.Context, ref string, options image.PullOptions) (io.ReadCloser, error) +} + +// Compile-time assertion to ensure docker.Client implements DockerClient. +var _ DockerClient = (*docker.Client)(nil) + +// Create global references to functions to allow for mocking in tests. +var dockerWaitUntilRunningFunc = dockerWaitUntilRunning + +type DockerManager struct { + gpus []string + modelDir string + overrides ImageOverrides + verboseLogs bool + + dockerClient DockerClient + // gpu ID => container name + gpuContainers map[string]string + // container name => container + containers map[string]*RunnerContainer + mu *sync.Mutex +} + +func NewDockerManager(overrides ImageOverrides, verboseLogs bool, gpus []string, modelDir string, client DockerClient) (*DockerManager, error) { + ctx, cancel := context.WithTimeout(context.Background(), containerTimeout) + if err := removeExistingContainers(ctx, client); err != nil { + cancel() + return nil, err + } + cancel() + + manager := &DockerManager{ + gpus: gpus, + modelDir: modelDir, + overrides: overrides, + verboseLogs: verboseLogs, + dockerClient: client, + gpuContainers: make(map[string]string), + containers: make(map[string]*RunnerContainer), + mu: &sync.Mutex{}, + } + + return manager, nil +} + +// EnsureImageAvailable ensures the container image is available locally for the given pipeline and model ID. +func (m *DockerManager) EnsureImageAvailable(ctx context.Context, pipeline string, modelID string) error { + imageName, err := m.getContainerImageName(pipeline, modelID) + if err != nil { + return err + } + + // Pull the image if it is not available locally. + if !m.isImageAvailable(ctx, pipeline, modelID) { + slog.Info(fmt.Sprintf("Pulling image for pipeline %s and modelID %s: %s", pipeline, modelID, imageName)) + err = m.pullImage(ctx, imageName) + if err != nil { + return err + } + } + + return nil +} + +func (m *DockerManager) Warm(ctx context.Context, pipeline string, modelID string, optimizationFlags OptimizationFlags) error { + m.mu.Lock() + defer m.mu.Unlock() + + _, err := m.createContainer(ctx, pipeline, modelID, true, optimizationFlags) + if err != nil { + return err + } + + return nil +} + +func (m *DockerManager) Stop(ctx context.Context) error { + var stopContainerWg sync.WaitGroup + for _, rc := range m.containers { + stopContainerWg.Add(1) + go func(container *RunnerContainer) { + defer stopContainerWg.Done() + m.destroyContainer(container, false) + }(rc) + } + + stopContainerWg.Wait() + return nil +} + +func (m *DockerManager) Borrow(ctx context.Context, pipeline, modelID string) (*RunnerContainer, error) { + m.mu.Lock() + defer m.mu.Unlock() + var rc *RunnerContainer + var err error + + for _, runner := range m.containers { + if runner.Pipeline == pipeline && runner.ModelID == modelID { + rc = runner + break + } + } + + // The container does not exist so try to create it + if rc == nil { + // TODO: Optimization flags for dynamically loaded (borrowed) containers are not currently supported due to startup delays. + rc, err = m.createContainer(ctx, pipeline, modelID, false, map[string]EnvValue{}) + if err != nil { + return nil, err + } + } + + // Remove container and set the BorrowCtx so it is unavailable until returnContainer() is called by watchContainer() + delete(m.containers, rc.Name) + rc.Lock() + rc.BorrowCtx = ctx + rc.Unlock() + + return rc, nil +} + +// returnContainer returns a container to the pool so it can be reused. It is called automatically by watchContainer +// when the BorrowCtx of the container is done or the container is IDLE. +func (m *DockerManager) returnContainer(rc *RunnerContainer) { + m.mu.Lock() + defer m.mu.Unlock() + + rc.Lock() + rc.BorrowCtx = nil + rc.Unlock() + + m.containers[rc.Name] = rc +} + +// getContainerImageName returns the image name for the given pipeline and model ID. +// Returns an error if the image is not found for "live-video-to-video". +func (m *DockerManager) getContainerImageName(pipeline, modelID string) (string, error) { + if pipeline == "live-video-to-video" { + // We currently use the model ID as the live pipeline name for legacy reasons. + if image, ok := m.overrides.Live[modelID]; ok { + return image, nil + } else if image, ok := livePipelineToImage[modelID]; ok { + return image, nil + } + return "", fmt.Errorf("no container image found for live pipeline %s", modelID) + } + + if image, ok := m.overrides.Batch[pipeline]; ok { + return image, nil + } else if image, ok := pipelineToImage[pipeline]; ok { + return image, nil + } + + if m.overrides.Default != "" { + return m.overrides.Default, nil + } + return defaultBaseImage, nil +} + +// HasCapacity checks if an unused managed container exists or if a GPU is available for a new container. +func (m *DockerManager) HasCapacity(ctx context.Context, pipeline, modelID string) bool { + m.mu.Lock() + defer m.mu.Unlock() + + // Check if unused managed container exists for the requested model. + for _, rc := range m.containers { + if rc.Pipeline == pipeline && rc.ModelID == modelID { + return true + } + } + + // TODO: This can be removed if we optimize the selection algorithm. + // Currently, using CreateContainer errors only can cause orchestrator reselection. + if pipeline != "live-video-to-video" && !m.isImageAvailable(ctx, pipeline, modelID) { + return false + } + + // Check for available GPU to allocate for a new container for the requested model. + _, err := m.allocGPU(ctx) + return err == nil +} + +// isImageAvailable checks if the specified image is available locally. +func (m *DockerManager) isImageAvailable(ctx context.Context, pipeline string, modelID string) bool { + imageName, err := m.getContainerImageName(pipeline, modelID) + if err != nil { + slog.Error(err.Error()) + return false + } + + _, _, err = m.dockerClient.ImageInspectWithRaw(ctx, imageName) + if err != nil { + slog.Error(fmt.Sprintf("Image for pipeline %s and modelID %s is not available locally: %s", pipeline, modelID, imageName)) + } + return err == nil +} + +// pullImage pulls the specified image from the registry. +func (m *DockerManager) pullImage(ctx context.Context, imageName string) error { + reader, err := m.dockerClient.ImagePull(ctx, imageName, image.PullOptions{}) + if err != nil { + return fmt.Errorf("failed to pull image: %w", err) + } + defer reader.Close() + + // Display progress messages from ImagePull reader. + decoder := json.NewDecoder(reader) + for { + var progress jsonmessage.JSONMessage + if err := decoder.Decode(&progress); err == io.EOF { + break + } else if err != nil { + return fmt.Errorf("error decoding progress message: %w", err) + } + if progress.Status != "" && progress.Progress != nil { + slog.Info(fmt.Sprintf("%s: %s", progress.Status, progress.Progress.String())) + } + } + + return nil +} + +func (m *DockerManager) createContainer(ctx context.Context, pipeline string, modelID string, keepWarm bool, optimizationFlags OptimizationFlags) (*RunnerContainer, error) { + gpu, err := m.allocGPU(ctx) + if err != nil { + return nil, err + } + + // NOTE: We currently allow only one container per GPU for each pipeline. + containerHostPort := containerHostPorts[pipeline][:3] + portOffset(gpu) + containerName := dockerContainerName(pipeline, modelID, containerHostPort) + containerImage, err := m.getContainerImageName(pipeline, modelID) + if err != nil { + return nil, err + } + + slog.Info("Starting managed container", slog.String("gpu", gpu), slog.String("name", containerName), slog.String("modelID", modelID), slog.String("containerImage", containerImage)) + + // Add optimization flags as environment variables. + envVars := []string{ + "PIPELINE=" + pipeline, + "MODEL_ID=" + modelID, + } + for key, value := range optimizationFlags { + envVars = append(envVars, key+"="+value.String()) + } + if m.verboseLogs { + envVars = append(envVars, "VERBOSE_LOGGING=1") + } + + containerConfig := &container.Config{ + Image: containerImage, + Env: envVars, + Volumes: map[string]struct{}{ + containerModelDir: {}, + }, + ExposedPorts: nat.PortSet{ + containerPort: struct{}{}, + }, + Labels: map[string]string{ + containerCreatorLabel: containerCreator, + }, + } + + gpuOpts := opts.GpuOpts{} + if !isEmulatedGPU(gpu) { + gpuOpts.Set("device=" + gpu) + } + + restartPolicy := container.RestartPolicy{ + Name: "on-failure", + MaximumRetryCount: 3, + } + if keepWarm { + restartPolicy = container.RestartPolicy{Name: "always"} + } + + hostConfig := &container.HostConfig{ + Resources: container.Resources{ + DeviceRequests: gpuOpts.Value(), + }, + Mounts: []mount.Mount{ + { + Type: mount.TypeBind, + Source: m.modelDir, + Target: containerModelDir, + }, + }, + PortBindings: nat.PortMap{ + containerPort: []nat.PortBinding{ + { + HostIP: "0.0.0.0", + HostPort: containerHostPort, + }, + }, + }, + RestartPolicy: restartPolicy, + } + + resp, err := m.dockerClient.ContainerCreate(ctx, containerConfig, hostConfig, nil, nil, containerName) + if err != nil { + return nil, err + } + + cctx, cancel := context.WithTimeout(ctx, containerTimeout) + if err := m.dockerClient.ContainerStart(cctx, resp.ID, container.StartOptions{}); err != nil { + cancel() + dockerRemoveContainer(m.dockerClient, resp.ID) + return nil, err + } + cancel() + + cctx, cancel = context.WithTimeout(ctx, containerTimeout) + if err := dockerWaitUntilRunningFunc(cctx, m.dockerClient, resp.ID, pollingInterval); err != nil { + cancel() + dockerRemoveContainer(m.dockerClient, resp.ID) + return nil, err + } + cancel() + + // Extend runner container timeout when optimization flags are used, as these + // pipelines may require more startup time. + runnerContainerTimeout := containerTimeout + if len(optimizationFlags) > 0 { + runnerContainerTimeout = optFlagsContainerTimeout + } + + cfg := RunnerContainerConfig{ + Type: Managed, + Pipeline: pipeline, + ModelID: modelID, + Endpoint: RunnerEndpoint{ + URL: "http://localhost:" + containerHostPort, + }, + ID: resp.ID, + GPU: gpu, + KeepWarm: keepWarm, + OptimizationFlags: optimizationFlags, + containerTimeout: runnerContainerTimeout, + } + + rc, err := NewRunnerContainer(ctx, cfg, containerName) + if err != nil { + dockerRemoveContainer(m.dockerClient, resp.ID) + return nil, err + } + + m.containers[containerName] = rc + m.gpuContainers[gpu] = containerName + + go m.watchContainer(rc) + + return rc, nil +} + +func (m *DockerManager) allocGPU(ctx context.Context) (string, error) { + // Is there a GPU available? + for _, gpu := range m.gpus { + _, ok := m.gpuContainers[gpu] + if !ok { + return gpu, nil + } + } + + // Is there a GPU with an idle container? + for _, gpu := range m.gpus { + containerName := m.gpuContainers[gpu] + // If the container exists in this map then it is idle and if it not marked as keep warm we remove it + rc, ok := m.containers[containerName] + if ok && !rc.KeepWarm { + if err := m.destroyContainer(rc, true); err != nil { + return "", err + } + return gpu, nil + } + } + + return "", errors.New("insufficient capacity") +} + +// destroyContainer stops the container on docker and removes it from the +// internal state. If locked is true then the mutex is not re-locked, otherwise +// it is done automatically only when updating the internal state. +func (m *DockerManager) destroyContainer(rc *RunnerContainer, locked bool) error { + slog.Info("Removing managed container", + slog.String("gpu", rc.GPU), + slog.String("name", rc.Name), + slog.String("modelID", rc.ModelID)) + + if err := dockerRemoveContainer(m.dockerClient, rc.ID); err != nil { + slog.Error("Error removing managed container", + slog.String("gpu", rc.GPU), + slog.String("name", rc.Name), + slog.String("modelID", rc.ModelID), + slog.String("error", err.Error())) + return fmt.Errorf("failed to remove container %s: %w", rc.Name, err) + } + + if !locked { + m.mu.Lock() + defer m.mu.Unlock() + } + delete(m.gpuContainers, rc.GPU) + delete(m.containers, rc.Name) + return nil +} + +// watchContainer monitors a container's running state and automatically cleans +// up the internal state when the container stops. It will also monitor the +// borrowCtx to return the container to the pool when it is done. +func (m *DockerManager) watchContainer(rc *RunnerContainer) { + defer func() { + if r := recover(); r != nil { + slog.Error("Panic in container watch routine", + slog.String("container", rc.Name), + slog.Any("panic", r), + slog.String("stack", string(debug.Stack()))) + } + }() + + ticker := time.NewTicker(containerWatchInterval) + defer ticker.Stop() + + slog.Info("Watching container", slog.String("container", rc.Name)) + failures := 0 + startTime := time.Now() + for { + if failures >= maxHealthCheckFailures && time.Since(startTime) > pipelineStartGracePeriod { + slog.Error("Container health check failed too many times", slog.String("container", rc.Name)) + m.destroyContainer(rc, false) + if rc.KeepWarm { + slog.Info("Container was kept warm, restarting", slog.String("container", rc.Name)) + err := m.Warm(context.Background(), rc.Pipeline, rc.ModelID, rc.OptimizationFlags) + if err != nil { + slog.Error("Error restarting warm container", slog.String("container", rc.Name), slog.String("error", err.Error())) + } + } + return + } + + rc.RLock() + borrowCtx := rc.BorrowCtx + rc.RUnlock() + + isBorrowed := borrowCtx != nil + // The BorrowCtx is set when the container has been borrowed for a request/stream. If it is not set (nil) it means + // that it's not currently borrowed, so we don't need to wait for it to be done (hence using the background context). + if borrowCtx == nil { + borrowCtx = context.Background() + } + + select { + case <-borrowCtx.Done(): + m.returnContainer(rc) + continue + case <-ticker.C: + ctx, cancel := context.WithTimeout(context.Background(), containerWatchInterval) + health, err := rc.Client.HealthWithResponse(ctx) + cancel() + + if err != nil { + failures++ + slog.Error("Error getting health for runner", + slog.String("container", rc.Name), + slog.String("error", err.Error())) + continue + } else if health.StatusCode() != 200 { + failures++ + slog.Error("Container health check failed with HTTP status code", + slog.String("container", rc.Name), + slog.Int("status_code", health.StatusCode()), + slog.String("body", string(health.Body))) + continue + } + slog.Debug("Health check response", + slog.String("status", health.Status()), + slog.Any("JSON200", health.JSON200), + slog.String("body", string(health.Body))) + + status := health.JSON200.Status + switch status { + case IDLE: + if isBorrowed && time.Since(startTime) > pipelineStartGracePeriod { + slog.Info("Container is idle, returning to pool", slog.String("container", rc.Name)) + m.returnContainer(rc) + continue + } + fallthrough + case OK: + failures = 0 + continue + default: + failures++ + slog.Error("Container not healthy", + slog.String("container", rc.Name), + slog.String("status", string(status)), + slog.String("failures", strconv.Itoa(failures))) + } + } + } +} + +func removeExistingContainers(ctx context.Context, client DockerClient) error { + filters := filters.NewArgs(filters.Arg("label", containerCreatorLabel+"="+containerCreator)) + containers, err := client.ContainerList(ctx, container.ListOptions{All: true, Filters: filters}) + if err != nil { + return err + } + + for _, c := range containers { + slog.Info("Removing existing managed container", slog.String("name", c.Names[0])) + if err := dockerRemoveContainer(client, c.ID); err != nil { + return err + } + } + + return nil +} + +// dockerContainerName generates a unique container name based on the pipeline, model ID, and an optional suffix. +func dockerContainerName(pipeline string, modelID string, suffix ...string) string { + sanitizedModelID := strings.NewReplacer("/", "-", "_", "-").Replace(modelID) + if len(suffix) > 0 { + return fmt.Sprintf("%s_%s_%s", pipeline, sanitizedModelID, suffix[0]) + } + return fmt.Sprintf("%s_%s", pipeline, sanitizedModelID) +} + +func dockerRemoveContainer(client DockerClient, containerID string) error { + ctx, cancel := context.WithTimeout(context.Background(), containerRemoveTimeout) + defer cancel() + + err := client.ContainerStop(ctx, containerID, container.StopOptions{}) + // Ignore "not found" or "already stopped" errors + if err != nil && !docker.IsErrNotFound(err) && !errdefs.IsNotModified(err) { + return err + } + + err = client.ContainerRemove(ctx, containerID, container.RemoveOptions{}) + if err == nil || docker.IsErrNotFound(err) { + return nil + } else if err != nil && !strings.Contains(err.Error(), "is already in progress") { + return err + } + // The container is being removed asynchronously, wait until it is actually gone + ticker := time.NewTicker(pollingInterval) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return fmt.Errorf("timed out waiting for container removal to complete") + case <-ticker.C: + _, err := client.ContainerInspect(ctx, containerID) + if docker.IsErrNotFound(err) { + return nil + } + } + } +} + +func dockerWaitUntilRunning(ctx context.Context, client DockerClient, containerID string, pollingInterval time.Duration) error { + ticker := time.NewTicker(pollingInterval) + defer ticker.Stop() + +tickerLoop: + for range ticker.C { + select { + case <-ctx.Done(): + return errors.New("timed out waiting for managed container") + default: + json, err := client.ContainerInspect(ctx, containerID) + if err != nil { + return err + } + + if json.State.Running { + break tickerLoop + } + } + } + + return nil +} + +func portOffset(gpu string) string { + if isEmulatedGPU(gpu) { + return strings.Replace(gpu, "emulated-", "", 1) + } + return gpu +} + +func isEmulatedGPU(gpu string) bool { + return strings.HasPrefix(gpu, "emulated-") +} diff --git a/ai/worker/docker_test.go b/ai/worker/docker_test.go new file mode 100644 index 0000000000..1a905aa823 --- /dev/null +++ b/ai/worker/docker_test.go @@ -0,0 +1,1092 @@ +package worker + +import ( + "context" + "fmt" + "io" + "net/http" + "net/http/httptest" + "strings" + "sync" + "testing" + "time" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/image" + "github.com/docker/docker/api/types/network" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +type MockDockerClient struct { + mock.Mock +} + +func (m *MockDockerClient) ImagePull(ctx context.Context, ref string, options image.PullOptions) (io.ReadCloser, error) { + args := m.Called(ctx, ref, options) + return args.Get(0).(io.ReadCloser), args.Error(1) +} + +func (m *MockDockerClient) ImageInspectWithRaw(ctx context.Context, imageID string) (types.ImageInspect, []byte, error) { + args := m.Called(ctx, imageID) + return args.Get(0).(types.ImageInspect), args.Get(1).([]byte), args.Error(2) +} + +func (m *MockDockerClient) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error) { + args := m.Called(ctx, config, hostConfig, networkingConfig, platform, containerName) + return args.Get(0).(container.CreateResponse), args.Error(1) +} + +func (m *MockDockerClient) ContainerStart(ctx context.Context, containerID string, options container.StartOptions) error { + args := m.Called(ctx, containerID, options) + return args.Error(0) +} + +func (m *MockDockerClient) ContainerInspect(ctx context.Context, containerID string) (types.ContainerJSON, error) { + args := m.Called(ctx, containerID) + return args.Get(0).(types.ContainerJSON), args.Error(1) +} + +func (m *MockDockerClient) ContainerList(ctx context.Context, options container.ListOptions) ([]types.Container, error) { + args := m.Called(ctx, options) + return args.Get(0).([]types.Container), args.Error(1) +} + +func (m *MockDockerClient) ContainerStop(ctx context.Context, containerID string, options container.StopOptions) error { + args := m.Called(ctx, containerID, options) + return args.Error(0) +} + +func (m *MockDockerClient) ContainerRemove(ctx context.Context, containerID string, options container.RemoveOptions) error { + args := m.Called(ctx, containerID, options) + return args.Error(0) +} + +type MockServer struct { + mock.Mock + *httptest.Server +} + +func (m *MockServer) ServeHTTP(method, path, reqBody string) (status int, contentType string, respBody string) { + args := m.Called(method, path, reqBody) + return args.Int(0), args.String(1), args.String(2) +} + +func NewMockServer() *MockServer { + mockServer := new(MockServer) + mockServer.Server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + method, path := r.Method, r.URL.Path + reqBody, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + status, contentType, respBody := mockServer.ServeHTTP(method, path, string(reqBody)) + + w.Header().Set("Content-Type", contentType) + w.WriteHeader(status) + w.Write([]byte(respBody)) + })) + return mockServer +} + +// createDockerManager creates a DockerManager with a mock DockerClient. +func createDockerManager(mockDockerClient *MockDockerClient) *DockerManager { + return &DockerManager{ + gpus: []string{"gpu0"}, + modelDir: "/models", + overrides: ImageOverrides{Default: "default-image"}, + dockerClient: mockDockerClient, + gpuContainers: make(map[string]string), + containers: make(map[string]*RunnerContainer), + mu: &sync.Mutex{}, + } +} + +func TestNewDockerManager(t *testing.T) { + mockDockerClient := new(MockDockerClient) + + createAndVerifyManager := func() *DockerManager { + manager, err := NewDockerManager(ImageOverrides{Default: "default-image"}, false, []string{"gpu0"}, "/models", mockDockerClient) + require.NoError(t, err) + require.NotNil(t, manager) + require.Equal(t, "default-image", manager.overrides.Default) + require.Equal(t, []string{"gpu0"}, manager.gpus) + require.Equal(t, "/models", manager.modelDir) + require.Equal(t, mockDockerClient, manager.dockerClient) + return manager + } + + t.Run("NoExistingContainers", func(t *testing.T) { + mockDockerClient.On("ContainerList", mock.Anything, mock.Anything).Return([]types.Container{}, nil).Once() + createAndVerifyManager() + mockDockerClient.AssertNotCalled(t, "ContainerStop", mock.Anything, mock.Anything, mock.Anything) + mockDockerClient.AssertNotCalled(t, "ContainerRemove", mock.Anything, mock.Anything, mock.Anything) + mockDockerClient.AssertExpectations(t) + }) + + t.Run("ExistingContainers", func(t *testing.T) { + // Mock client methods to simulate the removal of existing containers. + existingContainers := []types.Container{ + {ID: "container1", Names: []string{"/container1"}}, + {ID: "container2", Names: []string{"/container2"}}, + } + mockDockerClient.On("ContainerList", mock.Anything, mock.Anything).Return(existingContainers, nil) + mockDockerClient.On("ContainerStop", mock.Anything, "container1", mock.Anything).Return(nil) + mockDockerClient.On("ContainerStop", mock.Anything, "container2", mock.Anything).Return(nil) + mockDockerClient.On("ContainerRemove", mock.Anything, "container1", mock.Anything).Return(nil) + mockDockerClient.On("ContainerRemove", mock.Anything, "container2", mock.Anything).Return(nil) + + // Verify that existing containers were stopped and removed. + createAndVerifyManager() + mockDockerClient.AssertCalled(t, "ContainerStop", mock.Anything, "container1", mock.Anything) + mockDockerClient.AssertCalled(t, "ContainerStop", mock.Anything, "container2", mock.Anything) + mockDockerClient.AssertCalled(t, "ContainerRemove", mock.Anything, "container1", mock.Anything) + mockDockerClient.AssertCalled(t, "ContainerRemove", mock.Anything, "container2", mock.Anything) + mockDockerClient.AssertExpectations(t) + }) +} + +func TestDockerManager_EnsureImageAvailable(t *testing.T) { + mockDockerClient := new(MockDockerClient) + dockerManager := createDockerManager(mockDockerClient) + + ctx := context.Background() + pipeline := "text-to-image" + modelID := "test-model" + + tests := []struct { + name string + setup func(*DockerManager, *MockDockerClient) + expectedPull bool + }{ + { + name: "ImageAvailable", + setup: func(dockerManager *DockerManager, mockDockerClient *MockDockerClient) { + // Mock client methods to simulate the image being available locally. + mockDockerClient.On("ImageInspectWithRaw", mock.Anything, "default-image").Return(types.ImageInspect{}, []byte{}, nil).Once() + }, + expectedPull: false, + }, + { + name: "ImageNotAvailable", + setup: func(dockerManager *DockerManager, mockDockerClient *MockDockerClient) { + // Mock client methods to simulate the image not being available locally. + mockDockerClient.On("ImageInspectWithRaw", mock.Anything, "default-image").Return(types.ImageInspect{}, []byte{}, fmt.Errorf("image not found")).Once() + }, + expectedPull: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.setup(dockerManager, mockDockerClient) + + if tt.expectedPull { + mockDockerClient.On("ImagePull", mock.Anything, "default-image", mock.Anything).Return(io.NopCloser(strings.NewReader("")), nil).Once() + } + + err := dockerManager.EnsureImageAvailable(ctx, pipeline, modelID) + require.NoError(t, err) + + mockDockerClient.AssertExpectations(t) + }) + } +} + +func TestDockerManager_Warm(t *testing.T) { + mockDockerClient := new(MockDockerClient) + dockerManager := createDockerManager(mockDockerClient) + + ctx := context.Background() + pipeline := "text-to-image" + modelID := "test-model" + containerID := "container1" + optimizationFlags := OptimizationFlags{} + + // Mock nested functions. + originalFunc := dockerWaitUntilRunningFunc + dockerWaitUntilRunningFunc = func(ctx context.Context, client DockerClient, containerID string, pollingInterval time.Duration) error { + return nil + } + defer func() { dockerWaitUntilRunningFunc = originalFunc }() + originalFunc2 := runnerWaitUntilReadyFunc + runnerWaitUntilReadyFunc = func(ctx context.Context, client *ClientWithResponses, pollingInterval time.Duration) error { + return nil + } + defer func() { runnerWaitUntilReadyFunc = originalFunc2 }() + + mockDockerClient.On("ContainerCreate", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(container.CreateResponse{ID: containerID}, nil) + mockDockerClient.On("ContainerStart", mock.Anything, containerID, mock.Anything).Return(nil) + err := dockerManager.Warm(ctx, pipeline, modelID, optimizationFlags) + require.NoError(t, err) + mockDockerClient.AssertExpectations(t) +} + +func TestDockerManager_Stop(t *testing.T) { + MockDockerClient := new(MockDockerClient) + dockerManager := createDockerManager(MockDockerClient) + + ctx, cancel := context.WithTimeout(context.Background(), containerRemoveTimeout) + defer cancel() + containerID := "container1" + dockerManager.containers[containerID] = &RunnerContainer{ + RunnerContainerConfig: RunnerContainerConfig{ + ID: containerID, + }, + } + + MockDockerClient.On("ContainerStop", mock.Anything, containerID, container.StopOptions{Timeout: nil}).Return(nil) + MockDockerClient.On("ContainerRemove", mock.Anything, containerID, container.RemoveOptions{}).Return(nil) + err := dockerManager.Stop(ctx) + require.NoError(t, err) + MockDockerClient.AssertExpectations(t) +} + +func TestDockerManager_Borrow(t *testing.T) { + mockDockerClient := new(MockDockerClient) + dockerManager := createDockerManager(mockDockerClient) + + ctx := context.Background() + pipeline := "text-to-image" + modelID := "model" + containerID, _ := dockerManager.getContainerImageName(pipeline, modelID) + + // Mock nested functions. + originalFunc := dockerWaitUntilRunningFunc + dockerWaitUntilRunningFunc = func(ctx context.Context, client DockerClient, containerID string, pollingInterval time.Duration) error { + return nil + } + defer func() { dockerWaitUntilRunningFunc = originalFunc }() + originalFunc2 := runnerWaitUntilReadyFunc + runnerWaitUntilReadyFunc = func(ctx context.Context, client *ClientWithResponses, pollingInterval time.Duration) error { + return nil + } + defer func() { runnerWaitUntilReadyFunc = originalFunc2 }() + + mockDockerClient.On("ContainerCreate", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(container.CreateResponse{ID: containerID}, nil) + mockDockerClient.On("ContainerStart", mock.Anything, containerID, mock.Anything).Return(nil) + rc, err := dockerManager.Borrow(ctx, pipeline, modelID) + require.NoError(t, err) + require.NotNil(t, rc) + require.Empty(t, dockerManager.containers, "containers map should be empty") + mockDockerClient.AssertExpectations(t) +} + +func TestDockerManager_returnContainer(t *testing.T) { + mockDockerClient := new(MockDockerClient) + dockerManager := createDockerManager(mockDockerClient) + + // Create a RunnerContainer to return to the pool + rc := &RunnerContainer{ + Name: "container1", + RunnerContainerConfig: RunnerContainerConfig{}, + } + + // Ensure the container is not in the pool initially. + _, exists := dockerManager.containers[rc.Name] + require.False(t, exists) + + // Return the container to the pool. + dockerManager.returnContainer(rc) + + // Verify the container is now in the pool. + returnedContainer, exists := dockerManager.containers[rc.Name] + require.True(t, exists) + require.Equal(t, rc, returnedContainer) +} + +func TestDockerManager_getContainerImageName(t *testing.T) { + mockDockerClient := new(MockDockerClient) + dockerManager := createDockerManager(mockDockerClient) + + tests := []struct { + name string + setup func(*DockerManager, *MockDockerClient) + pipeline string + modelID string + expectedImage string + expectError bool + }{ + { + name: "live-video-to-video with valid modelID", + setup: func(dockerManager *DockerManager, mockDockerClient *MockDockerClient) {}, + pipeline: "live-video-to-video", + modelID: "streamdiffusion", + expectedImage: "livepeer/ai-runner:live-app-streamdiffusion", + expectError: false, + }, + { + name: "live-video-to-video with invalid modelID", + setup: func(dockerManager *DockerManager, mockDockerClient *MockDockerClient) {}, + pipeline: "live-video-to-video", + modelID: "invalid-model", + expectError: true, + }, + { + name: "valid pipeline", + setup: func(dockerManager *DockerManager, mockDockerClient *MockDockerClient) {}, + pipeline: "text-to-speech", + modelID: "", + expectedImage: "livepeer/ai-runner:text-to-speech", + expectError: false, + }, + { + name: "invalid pipeline", + setup: func(dockerManager *DockerManager, mockDockerClient *MockDockerClient) {}, + pipeline: "invalid-pipeline", + modelID: "", + expectedImage: "default-image", + expectError: false, + }, + { + name: "override default image", + setup: func(dockerManager *DockerManager, mockDockerClient *MockDockerClient) { + dockerManager.overrides = ImageOverrides{ + Default: "custom-image", + } + }, + pipeline: "", + modelID: "", + expectedImage: "custom-image", + expectError: false, + }, + { + name: "override batch image", + setup: func(dockerManager *DockerManager, mockDockerClient *MockDockerClient) { + dockerManager.overrides = ImageOverrides{ + Batch: map[string]string{ + "text-to-speech": "custom-image", + }, + } + }, + pipeline: "text-to-speech", + modelID: "", + expectedImage: "custom-image", + expectError: false, + }, + { + name: "override live image", + setup: func(dockerManager *DockerManager, mockDockerClient *MockDockerClient) { + dockerManager.overrides = ImageOverrides{ + Live: map[string]string{ + "streamdiffusion": "custom-image", + }, + } + }, + pipeline: "live-video-to-video", + modelID: "streamdiffusion", + expectedImage: "custom-image", + expectError: false, + }, + { + name: "non-overridden batch image", + setup: func(dockerManager *DockerManager, mockDockerClient *MockDockerClient) { + dockerManager.overrides = ImageOverrides{ + Default: "default-image", + Batch: map[string]string{ + "text-to-speech": "custom-batch-image", + }, + Live: map[string]string{ + "streamdiffusion": "custom-live-image", + }, + } + }, + pipeline: "audio-to-text", + modelID: "", + expectedImage: "livepeer/ai-runner:audio-to-text", + expectError: false, + }, + { + name: "non-overridden live image", + setup: func(dockerManager *DockerManager, mockDockerClient *MockDockerClient) { + dockerManager.overrides = ImageOverrides{ + Default: "default-image", + Batch: map[string]string{ + "text-to-speech": "custom-batch-image", + }, + Live: map[string]string{ + "streamdiffusion": "custom-live-image", + }, + } + }, + pipeline: "live-video-to-video", + modelID: "comfyui", + expectedImage: "livepeer/ai-runner:live-app-comfyui", + expectError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.setup(dockerManager, mockDockerClient) + + image, err := dockerManager.getContainerImageName(tt.pipeline, tt.modelID) + if tt.expectError { + require.Error(t, err) + require.Equal(t, fmt.Sprintf("no container image found for live pipeline %s", tt.modelID), err.Error()) + } else { + require.NoError(t, err) + require.Equal(t, tt.expectedImage, image) + } + }) + } +} + +func TestDockerManager_HasCapacity(t *testing.T) { + ctx := context.Background() + pipeline := "text-to-image" + modelID := "test-model" + + tests := []struct { + name string + setup func(*DockerManager, *MockDockerClient) + expectedHasCapacity bool + }{ + { + name: "UnusedManagedContainerExists", + setup: func(dockerManager *DockerManager, mockDockerClient *MockDockerClient) { + // Add an unused managed container. + dockerManager.containers["container1"] = &RunnerContainer{ + RunnerContainerConfig: RunnerContainerConfig{ + Pipeline: pipeline, + ModelID: modelID, + }} + }, + expectedHasCapacity: true, + }, + { + name: "ImageNotAvailable", + setup: func(dockerManager *DockerManager, mockDockerClient *MockDockerClient) { + // Mock client methods to simulate the image not being available locally. + mockDockerClient.On("ImageInspectWithRaw", mock.Anything, "default-image").Return(types.ImageInspect{}, []byte{}, fmt.Errorf("image not found")) + }, + expectedHasCapacity: false, + }, + { + name: "GPUAvailable", + setup: func(dockerManager *DockerManager, mockDockerClient *MockDockerClient) { + // Mock client methods to simulate the image being available locally. + mockDockerClient.On("ImageInspectWithRaw", mock.Anything, "default-image").Return(types.ImageInspect{}, []byte{}, nil) + // Ensure that the GPU is available by not setting any container for the GPU. + dockerManager.gpuContainers = make(map[string]string) + }, + expectedHasCapacity: true, + }, + { + name: "GPUUnavailable", + setup: func(dockerManager *DockerManager, mockDockerClient *MockDockerClient) { + // Mock client methods to simulate the image being available locally. + mockDockerClient.On("ImageInspectWithRaw", mock.Anything, "default-image").Return(types.ImageInspect{}, []byte{}, nil) + // Ensure that the GPU is not available by setting a container for the GPU. + dockerManager.gpuContainers["gpu0"] = "container1" + }, + expectedHasCapacity: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockDockerClient := new(MockDockerClient) + dockerManager := createDockerManager(mockDockerClient) + + tt.setup(dockerManager, mockDockerClient) + + hasCapacity := dockerManager.HasCapacity(ctx, pipeline, modelID) + require.Equal(t, tt.expectedHasCapacity, hasCapacity) + + mockDockerClient.AssertExpectations(t) + }) + } +} + +func TestDockerManager_isImageAvailable(t *testing.T) { + mockDockerClient := new(MockDockerClient) + dockerManager := createDockerManager(mockDockerClient) + + ctx := context.Background() + pipeline := "text-to-image" + modelID := "test-model" + + t.Run("ImageNotFound", func(t *testing.T) { + mockDockerClient.On("ImageInspectWithRaw", mock.Anything, "default-image").Return(types.ImageInspect{}, []byte{}, fmt.Errorf("image not found")).Once() + + isAvailable := dockerManager.isImageAvailable(ctx, pipeline, modelID) + require.False(t, isAvailable) + mockDockerClient.AssertExpectations(t) + }) + + t.Run("ImageFound", func(t *testing.T) { + mockDockerClient.On("ImageInspectWithRaw", mock.Anything, "default-image").Return(types.ImageInspect{}, []byte{}, nil).Once() + + isAvailable := dockerManager.isImageAvailable(ctx, pipeline, modelID) + require.True(t, isAvailable) + mockDockerClient.AssertExpectations(t) + }) +} + +func TestDockerManager_pullImage(t *testing.T) { + mockDockerClient := new(MockDockerClient) + dockerManager := createDockerManager(mockDockerClient) + + ctx := context.Background() + imageName := "default-image" + + t.Run("ImagePullError", func(t *testing.T) { + mockDockerClient.On("ImagePull", mock.Anything, imageName, mock.Anything).Return(io.NopCloser(strings.NewReader("")), fmt.Errorf("failed to pull image: pull error")).Once() + + err := dockerManager.pullImage(ctx, imageName) + require.Error(t, err) + require.Contains(t, err.Error(), "failed to pull image: pull error") + mockDockerClient.AssertExpectations(t) + }) + + t.Run("ImagePullSuccess", func(t *testing.T) { + mockDockerClient.On("ImagePull", mock.Anything, imageName, mock.Anything).Return(io.NopCloser(strings.NewReader("")), nil).Once() + + err := dockerManager.pullImage(ctx, imageName) + require.NoError(t, err) + mockDockerClient.AssertExpectations(t) + }) +} + +func TestDockerManager_createContainer(t *testing.T) { + mockDockerClient := new(MockDockerClient) + dockerManager := createDockerManager(mockDockerClient) + + ctx := context.Background() + pipeline := "text-to-image" + modelID := "test-model" + containerID := "container1" + gpu := "0" + containerHostPort := "8000" + containerName := dockerContainerName(pipeline, modelID, containerHostPort) + containerImage := "default-image" + optimizationFlags := OptimizationFlags{} + + // Mock nested functions. + originalFunc := dockerWaitUntilRunningFunc + dockerWaitUntilRunningFunc = func(ctx context.Context, client DockerClient, containerID string, pollingInterval time.Duration) error { + return nil + } + defer func() { dockerWaitUntilRunningFunc = originalFunc }() + originalFunc2 := runnerWaitUntilReadyFunc + runnerWaitUntilReadyFunc = func(ctx context.Context, client *ClientWithResponses, pollingInterval time.Duration) error { + return nil + } + defer func() { runnerWaitUntilReadyFunc = originalFunc2 }() + + // Mock allocGPU and getContainerImageName methods. + dockerManager.gpus = []string{gpu} + dockerManager.gpuContainers = make(map[string]string) + dockerManager.containers = make(map[string]*RunnerContainer) + dockerManager.overrides.Default = containerImage + + mockDockerClient.On("ContainerCreate", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(container.CreateResponse{ID: containerID}, nil) + mockDockerClient.On("ContainerStart", mock.Anything, containerID, mock.Anything).Return(nil) + + rc, err := dockerManager.createContainer(ctx, pipeline, modelID, false, optimizationFlags) + require.NoError(t, err) + require.NotNil(t, rc) + require.Equal(t, containerID, rc.ID) + require.Equal(t, gpu, rc.GPU) + require.Equal(t, pipeline, rc.Pipeline) + require.Equal(t, modelID, rc.ModelID) + require.Equal(t, containerName, rc.Name) + + mockDockerClient.AssertExpectations(t) +} + +func TestDockerManager_allocGPU(t *testing.T) { + ctx := context.Background() + + tests := []struct { + name string + setup func(*DockerManager, *MockDockerClient) + expectedAllocatedGPU string + errorMessage string + }{ + { + name: "GPUAvailable", + setup: func(dockerManager *DockerManager, mockDockerClient *MockDockerClient) { + // Ensure that the GPU is available by not setting any container for the GPU. + dockerManager.gpuContainers = make(map[string]string) + }, + expectedAllocatedGPU: "gpu0", + errorMessage: "", + }, + { + name: "GPUUnavailable", + setup: func(dockerManager *DockerManager, mockDockerClient *MockDockerClient) { + // Ensure that the GPU is not available by setting a container for the GPU. + dockerManager.gpuContainers["gpu0"] = "container1" + }, + expectedAllocatedGPU: "", + errorMessage: "insufficient capacity", + }, + { + name: "GPUUnavailableAndWarm", + setup: func(dockerManager *DockerManager, mockDockerClient *MockDockerClient) { + // Ensure that the GPU is not available by setting a container for the GPU. + dockerManager.gpuContainers["gpu0"] = "container1" + dockerManager.containers["container1"] = &RunnerContainer{ + RunnerContainerConfig: RunnerContainerConfig{ + ID: "container1", + KeepWarm: true, + }, + } + }, + expectedAllocatedGPU: "", + errorMessage: "insufficient capacity", + }, + { + name: "GPUUnavailableButCold", + setup: func(dockerManager *DockerManager, mockDockerClient *MockDockerClient) { + // Ensure that the GPU is not available by setting a container for the GPU. + dockerManager.gpuContainers["gpu0"] = "container1" + dockerManager.containers["container1"] = &RunnerContainer{ + RunnerContainerConfig: RunnerContainerConfig{ + ID: "container1", + KeepWarm: false, + }, + } + // Mock client methods to simulate the removal of the warm container. + mockDockerClient.On("ContainerStop", mock.Anything, "container1", container.StopOptions{}).Return(nil) + mockDockerClient.On("ContainerRemove", mock.Anything, "container1", container.RemoveOptions{}).Return(nil) + }, + expectedAllocatedGPU: "gpu0", + errorMessage: "", + }, + } + + for _, tt := range tests { + mockDockerClient := new(MockDockerClient) + dockerManager := createDockerManager(mockDockerClient) + + tt.setup(dockerManager, mockDockerClient) + + gpu, err := dockerManager.allocGPU(ctx) + if tt.errorMessage != "" { + require.Error(t, err) + require.Contains(t, err.Error(), tt.errorMessage) + } else { + require.NoError(t, err) + require.Equal(t, tt.expectedAllocatedGPU, gpu) + } + } +} + +func TestDockerManager_destroyContainer(t *testing.T) { + mockDockerClient := new(MockDockerClient) + dockerManager := createDockerManager(mockDockerClient) + + containerID := "container1" + gpu := "gpu0" + + rc := &RunnerContainer{ + Name: containerID, + RunnerContainerConfig: RunnerContainerConfig{ + ID: containerID, + GPU: gpu, + }, + } + dockerManager.gpuContainers[gpu] = containerID + dockerManager.containers[containerID] = rc + + mockDockerClient.On("ContainerStop", mock.Anything, containerID, container.StopOptions{}).Return(nil) + mockDockerClient.On("ContainerRemove", mock.Anything, containerID, container.RemoveOptions{}).Return(nil) + + err := dockerManager.destroyContainer(rc, true) + require.NoError(t, err) + require.Empty(t, dockerManager.gpuContainers, "gpuContainers map should be empty") + require.Empty(t, dockerManager.containers, "containers map should be empty") + mockDockerClient.AssertExpectations(t) +} + +func TestDockerManager_watchContainer(t *testing.T) { + // Override the containerWatchInterval for testing purposes. + originalContainerWatchInterval, originalPipelineStartGracePeriod := containerWatchInterval, pipelineStartGracePeriod + containerWatchInterval = 10 * time.Millisecond + pipelineStartGracePeriod = 0 + defer func() { + containerWatchInterval, pipelineStartGracePeriod = originalContainerWatchInterval, originalPipelineStartGracePeriod + }() + + setup := func() (*MockDockerClient, *DockerManager, *MockServer, *RunnerContainer) { + mockDockerClient := new(MockDockerClient) + dockerManager := createDockerManager(mockDockerClient) + + mockServer := NewMockServer() + mockClient, err := NewClientWithResponses(mockServer.URL) + require.NoError(t, err) + + containerID := "container1" + rc := &RunnerContainer{ + Name: containerID, + RunnerContainerConfig: RunnerContainerConfig{ + ID: containerID, + }, + Client: mockClient, + } + + return mockDockerClient, dockerManager, mockServer, rc + } + + t.Run("ReturnContainerOnContextDone", func(t *testing.T) { + mockDockerClient, dockerManager, mockServer, rc := setup() + defer mockServer.Close() + defer mockServer.AssertExpectations(t) + defer mockDockerClient.AssertNotCalled(t, "ContainerRemove", mock.Anything, rc.Name, mock.Anything) + + mockServer.On("ServeHTTP", "GET", "/health", mock.Anything). + Return(200, "application/json", `{"status":"OK"}`) + + borrowCtx, cancel := context.WithCancel(context.Background()) + rc.BorrowCtx = borrowCtx + + watchingCtx, stopWatching := context.WithCancel(context.Background()) + go func() { + dockerManager.watchContainer(rc) + stopWatching() + }() + cancel() // Cancel the context. + time.Sleep(50 * time.Millisecond) // Ensure the ticker triggers. + + // Verify that the container was returned. + _, exists := dockerManager.containers[rc.Name] + require.True(t, exists) + + // Watch should keep running + require.Nil(t, watchingCtx.Err()) + + // Borrow the container again + borrowCtx, cancel = context.WithCancel(context.Background()) + delete(dockerManager.containers, rc.Name) + rc.Lock() + rc.BorrowCtx = borrowCtx + rc.Unlock() + + cancel() // Cancel the borrow context + time.Sleep(50 * time.Millisecond) // Ensure the ticker triggers. + + // Verify that the container was returned. + _, exists = dockerManager.containers[rc.Name] + require.True(t, exists) + + // Watch should still keep running + require.Nil(t, watchingCtx.Err()) + }) + + notHealthyTestCases := []struct { + name string + mockServerSetup func(*MockServer) + }{ + { + name: "ErrorStatus", + mockServerSetup: func(mockServer *MockServer) { + mockServer.On("ServeHTTP", "GET", "/health", mock.Anything). + Return(200, "application/json", `{"status":"ERROR"}`). + Times(2) + }, + }, + { + name: "UnknownStatus", + mockServerSetup: func(mockServer *MockServer) { + mockServer.On("ServeHTTP", "GET", "/health", mock.Anything). + Return(200, "application/json", `{"status":"HAPPY"}`). + Times(2) + }, + }, + { + name: "BadSchema", + mockServerSetup: func(mockServer *MockServer) { + mockServer.On("ServeHTTP", "GET", "/health", mock.Anything). + Return(200, "application/json", `{"status":1}`). + Times(2) + }, + }, + { + name: "HTTPError", + mockServerSetup: func(mockServer *MockServer) { + mockServer.On("ServeHTTP", "GET", "/health", mock.Anything). + Return(500, "text/plain", "Error"). + Times(2) + }, + }, + { + name: "Timeout", + mockServerSetup: func(mockServer *MockServer) { + done := make(chan time.Time) + go func() { + time.Sleep(100 * time.Millisecond) + close(done) + }() + mockServer.On("ServeHTTP", "GET", "/health", mock.Anything). + WaitUntil(done). + Return(200, "application/json", `{"status":"OK"}`). + Times(2) + }, + }, + { + name: "ConnectionRefused", + mockServerSetup: func(mockServer *MockServer) { + mockServer.Close() + }, + }, + } + for _, tt := range notHealthyTestCases { + t.Run("DestroyContainerOnNotHealthy_"+tt.name, func(t *testing.T) { + mockDockerClient, dockerManager, mockServer, rc := setup() + defer mockServer.Close() + defer mockDockerClient.AssertExpectations(t) + defer mockServer.AssertExpectations(t) + + tt.mockServerSetup(mockServer) + + // Mock destroyContainer to verify it is called. + mockDockerClient.On("ContainerStop", mock.Anything, rc.Name, mock.Anything).Return(nil).Once() + mockDockerClient.On("ContainerRemove", mock.Anything, rc.Name, mock.Anything).Return(nil).Once() + + done := make(chan struct{}) + go func() { + defer close(done) + dockerManager.watchContainer(rc) + }() + select { + case <-done: + case <-time.After(50 * time.Millisecond): + t.Fatal("watchContainer did not return") + } + + // Verify that the container was destroyed. + _, exists := dockerManager.containers[rc.Name] + require.False(t, exists) + }) + } + + t.Run("RespectStartupGracePeriod", func(t *testing.T) { + pipelineStartGracePeriod = 50 * time.Millisecond + defer func() { pipelineStartGracePeriod = 0 }() + + mockDockerClient, dockerManager, mockServer, rc := setup() + defer mockServer.Close() + defer mockDockerClient.AssertExpectations(t) + defer mockServer.AssertExpectations(t) + + // Must fail twice before the watch routine gives up on it + mockServer.On("ServeHTTP", "GET", "/health", mock.Anything). + Return(200, "application/json", `{"status":"ERROR"}`). + Times(4) // 4 calls during the grace period (first call only after 10ms) + + go dockerManager.watchContainer(rc) + time.Sleep(40 * time.Millisecond) // Almost the entire grace period + + // Make sure container wasn't destroyed yet + mockDockerClient.AssertNotCalled(t, "ContainerRemove", mock.Anything, rc.Name, mock.Anything) + + // Mock destroyContainer to verify it is called. + mockDockerClient.On("ContainerStop", mock.Anything, rc.Name, mock.Anything).Return(nil).Once() + mockDockerClient.On("ContainerRemove", mock.Anything, rc.Name, mock.Anything).Return(nil).Once() + + time.Sleep(30 * time.Millisecond) // Ensure we pass the grace period + + // Verify that the container was destroyed. + _, exists := dockerManager.containers[rc.Name] + require.False(t, exists) + }) + + t.Run("ReturnContainerWhenIdle", func(t *testing.T) { + pipelineStartGracePeriod = 50 * time.Millisecond + defer func() { pipelineStartGracePeriod = 0 }() + + mockDockerClient, dockerManager, mockServer, rc := setup() + defer mockServer.Close() + defer mockServer.AssertExpectations(t) + defer mockDockerClient.AssertNotCalled(t, "ContainerRemove", mock.Anything, rc.Name, mock.Anything) + + // Schedule: + // - Return IDLE for the first 3 times during grace period (should not return) + // - Then OK for the next 5 times (stayin' alive) + // - Then back to IDLE (should return) + mockServer.On("ServeHTTP", "GET", "/health", mock.Anything). + Return(200, "application/json", `{"status":"IDLE"}`). + Times(3). + On("ServeHTTP", "GET", "/health", mock.Anything). + Return(200, "application/json", `{"status":"OK"}`). + Times(5). + On("ServeHTTP", "GET", "/health", mock.Anything). + Return(200, "application/json", `{"status":"IDLE"}`). + Once() // once is enough and container should be returned immediately + + startTime := time.Now() + sleepUntil := func(sinceStart time.Duration) { + dur := sinceStart - time.Since(startTime) + if dur > 0 { + time.Sleep(dur) + } + } + rc.BorrowCtx = context.Background() // Simulate a borrow of the container + go dockerManager.watchContainer(rc) + sleepUntil(30 * time.Millisecond) // Almost the entire grace period + + // Verify that the container was not returned yet. + _, exists := dockerManager.containers[rc.Name] + require.False(t, exists) + + sleepUntil(60 * time.Millisecond) // Ensure we pass the grace period and container is OK + + // Verify that the container was not returned yet. + _, exists = dockerManager.containers[rc.Name] + require.False(t, exists) + + sleepUntil(100 * time.Millisecond) // Ensure we pass the grace period and container is now IDLE and returned + + // Verify that the container was returned. + _, exists = dockerManager.containers[rc.Name] + require.True(t, exists) + }) +} + +// Watch container + +func TestRemoveExistingContainers(t *testing.T) { + mockDockerClient := new(MockDockerClient) + + ctx := context.Background() + + // Mock client methods to simulate the removal of existing containers. + existingContainers := []types.Container{ + {ID: "container1", Names: []string{"/container1"}}, + {ID: "container2", Names: []string{"/container2"}}, + } + mockDockerClient.On("ContainerList", mock.Anything, mock.Anything).Return(existingContainers, nil) + mockDockerClient.On("ContainerStop", mock.Anything, "container1", mock.Anything).Return(nil) + mockDockerClient.On("ContainerStop", mock.Anything, "container2", mock.Anything).Return(nil) + mockDockerClient.On("ContainerRemove", mock.Anything, "container1", mock.Anything).Return(nil) + mockDockerClient.On("ContainerRemove", mock.Anything, "container2", mock.Anything).Return(nil) + + removeExistingContainers(ctx, mockDockerClient) + mockDockerClient.AssertExpectations(t) +} + +func TestDockerContainerName(t *testing.T) { + tests := []struct { + name string + pipeline string + modelID string + suffix []string + expectedName string + }{ + { + name: "with suffix", + pipeline: "text-to-speech", + modelID: "model1", + suffix: []string{"suffix1"}, + expectedName: "text-to-speech_model1_suffix1", + }, + { + name: "without suffix", + pipeline: "text-to-speech", + modelID: "model1", + expectedName: "text-to-speech_model1", + }, + { + name: "modelID with special characters", + pipeline: "text-to-speech", + modelID: "model/1_2", + suffix: []string{"suffix1"}, + expectedName: "text-to-speech_model-1-2_suffix1", + }, + { + name: "modelID with special characters without suffix", + pipeline: "text-to-speech", + modelID: "model/1_2", + expectedName: "text-to-speech_model-1-2", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + name := dockerContainerName(tt.pipeline, tt.modelID, tt.suffix...) + require.Equal(t, tt.expectedName, name) + }) + } +} + +func TestDockerRemoveContainer(t *testing.T) { + mockDockerClient := new(MockDockerClient) + + mockDockerClient.On("ContainerStop", mock.Anything, "container1", container.StopOptions{}).Return(nil) + mockDockerClient.On("ContainerRemove", mock.Anything, "container1", container.RemoveOptions{}).Return(nil) + + err := dockerRemoveContainer(mockDockerClient, "container1") + require.NoError(t, err) + mockDockerClient.AssertExpectations(t) +} + +func TestDockerWaitUntilRunning(t *testing.T) { + mockDockerClient := new(MockDockerClient) + containerID := "container1" + pollingInterval := 10 * time.Millisecond + ctx := context.Background() + + t.Run("ContainerRunning", func(t *testing.T) { + // Mock ContainerInspect to return a running container state. + mockDockerClient.On("ContainerInspect", mock.Anything, containerID).Return(types.ContainerJSON{ + ContainerJSONBase: &types.ContainerJSONBase{ + State: &types.ContainerState{ + Running: true, + }, + }, + }, nil).Once() + + err := dockerWaitUntilRunning(ctx, mockDockerClient, containerID, pollingInterval) + require.NoError(t, err) + mockDockerClient.AssertExpectations(t) + }) + + t.Run("ContainerNotRunningInitially", func(t *testing.T) { + // Mock ContainerInspect to return a non-running state initially, then a running state. + mockDockerClient.On("ContainerInspect", mock.Anything, containerID).Return(types.ContainerJSON{ + ContainerJSONBase: &types.ContainerJSONBase{ + State: &types.ContainerState{ + Running: false, + }, + }, + }, nil).Once() + mockDockerClient.On("ContainerInspect", mock.Anything, containerID).Return(types.ContainerJSON{ + ContainerJSONBase: &types.ContainerJSONBase{ + State: &types.ContainerState{ + Running: true, + }, + }, + }, nil).Once() + + err := dockerWaitUntilRunning(ctx, mockDockerClient, containerID, pollingInterval) + require.NoError(t, err) + mockDockerClient.AssertExpectations(t) + }) + + t.Run("ContextTimeout", func(t *testing.T) { + // Create a context that will timeout. + timeoutCtx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) + defer cancel() + + // Mock ContainerInspect to always return a non-running state. + mockDockerClient.On("ContainerInspect", mock.Anything, containerID).Return(types.ContainerJSON{ + ContainerJSONBase: &types.ContainerJSONBase{ + State: &types.ContainerState{ + Running: false, + }, + }, + }, nil) + + err := dockerWaitUntilRunning(timeoutCtx, mockDockerClient, containerID, pollingInterval) + require.Error(t, err) + require.Contains(t, err.Error(), "timed out waiting for managed container") + mockDockerClient.AssertExpectations(t) + }) +} diff --git a/ai/worker/multipart.go b/ai/worker/multipart.go new file mode 100644 index 0000000000..bc70ba8f4b --- /dev/null +++ b/ai/worker/multipart.go @@ -0,0 +1,365 @@ +/* +Package worker's module provides multipart form data utilities. + +This module extends oapi-codegen Go bindings with multipart writers, enabling request +parameter encoding for inter-node communication (gateway, orchestrator, runner). +*/ +package worker + +import ( + "fmt" + "io" + "mime/multipart" + "strconv" +) + +func NewImageToImageMultipartWriter(w io.Writer, req GenImageToImageMultipartRequestBody) (*multipart.Writer, error) { + mw := multipart.NewWriter(w) + 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 req.ModelId != nil { + if err := mw.WriteField("model_id", *req.ModelId); err != nil { + return nil, err + } + } + if req.Loras != nil { + if err := mw.WriteField("loras", *req.Loras); err != nil { + return nil, err + } + } + if req.Strength != nil { + if err := mw.WriteField("strength", fmt.Sprintf("%f", *req.Strength)); err != nil { + return nil, err + } + } + if req.GuidanceScale != nil { + if err := mw.WriteField("guidance_scale", fmt.Sprintf("%f", *req.GuidanceScale)); err != nil { + return nil, err + } + } + if req.ImageGuidanceScale != nil { + if err := mw.WriteField("image_guidance_scale", fmt.Sprintf("%f", *req.ImageGuidanceScale)); err != nil { + return nil, err + } + } + if req.NegativePrompt != nil { + if err := mw.WriteField("negative_prompt", *req.NegativePrompt); err != nil { + return nil, err + } + } + if req.SafetyCheck != nil { + if err := mw.WriteField("safety_check", strconv.FormatBool(*req.SafetyCheck)); err != nil { + return nil, err + } + } + if req.Seed != nil { + if err := mw.WriteField("seed", strconv.Itoa(*req.Seed)); err != nil { + return nil, err + } + } + if req.NumImagesPerPrompt != nil { + if err := mw.WriteField("num_images_per_prompt", strconv.Itoa(*req.NumImagesPerPrompt)); err != nil { + return nil, err + } + } + if req.NumInferenceSteps != nil { + if err := mw.WriteField("num_inference_steps", strconv.Itoa(*req.NumInferenceSteps)); err != nil { + return nil, err + } + } + + if err := mw.Close(); err != nil { + return nil, err + } + + return mw, nil +} + +func NewImageToVideoMultipartWriter(w io.Writer, req GenImageToVideoMultipartRequestBody) (*multipart.Writer, error) { + mw := multipart.NewWriter(w) + 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 req.ModelId != nil { + if err := mw.WriteField("model_id", *req.ModelId); err != nil { + return nil, err + } + } + if req.Height != nil { + if err := mw.WriteField("height", strconv.Itoa(*req.Height)); err != nil { + return nil, err + } + } + if req.Width != nil { + if err := mw.WriteField("width", strconv.Itoa(*req.Width)); err != nil { + return nil, err + } + } + if req.Fps != nil { + if err := mw.WriteField("fps", strconv.Itoa(*req.Fps)); err != nil { + return nil, err + } + } + if req.MotionBucketId != nil { + if err := mw.WriteField("motion_bucket_id", strconv.Itoa(*req.MotionBucketId)); err != nil { + return nil, err + } + } + if req.NoiseAugStrength != nil { + if err := mw.WriteField("noise_aug_strength", fmt.Sprintf("%f", *req.NoiseAugStrength)); err != nil { + return nil, err + } + } + if req.Seed != nil { + if err := mw.WriteField("seed", strconv.Itoa(*req.Seed)); err != nil { + return nil, err + } + } + if req.SafetyCheck != nil { + if err := mw.WriteField("safety_check", strconv.FormatBool(*req.SafetyCheck)); err != nil { + return nil, err + } + } + if req.NumInferenceSteps != nil { + if err := mw.WriteField("num_inference_steps", strconv.Itoa(*req.NumInferenceSteps)); err != nil { + return nil, err + } + } + + if err := mw.Close(); err != nil { + return nil, err + } + + return mw, nil +} + +func NewUpscaleMultipartWriter(w io.Writer, req GenUpscaleMultipartRequestBody) (*multipart.Writer, error) { + mw := multipart.NewWriter(w) + 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 req.ModelId != nil { + if err := mw.WriteField("model_id", *req.ModelId); err != nil { + return nil, err + } + } + if req.SafetyCheck != nil { + if err := mw.WriteField("safety_check", strconv.FormatBool(*req.SafetyCheck)); err != nil { + return nil, err + } + } + if req.Seed != nil { + if err := mw.WriteField("seed", strconv.Itoa(*req.Seed)); err != nil { + return nil, err + } + } + if req.NumInferenceSteps != nil { + if err := mw.WriteField("num_inference_steps", strconv.Itoa(*req.NumInferenceSteps)); err != nil { + return nil, err + } + } + + if err := mw.Close(); err != nil { + return nil, err + } + + return mw, nil +} + +func NewAudioToTextMultipartWriter(w io.Writer, req GenAudioToTextMultipartRequestBody) (*multipart.Writer, error) { + mw := multipart.NewWriter(w) + writer, err := mw.CreateFormFile("audio", req.Audio.Filename()) + if err != nil { + return nil, err + } + audioSize := req.Audio.FileSize() + audioRdr, err := req.Audio.Reader() + if err != nil { + return nil, err + } + copied, err := io.Copy(writer, audioRdr) + if err != nil { + return nil, err + } + if copied != audioSize { + return nil, fmt.Errorf("failed to copy audio to multipart request audioBytes=%v copiedBytes=%v", audioSize, copied) + } + + if req.ModelId != nil { + if err := mw.WriteField("model_id", *req.ModelId); err != nil { + return nil, err + } + } + + if req.ReturnTimestamps != nil { + if err := mw.WriteField("return_timestamps", *req.ReturnTimestamps); err != nil { + return nil, err + } + } + + if req.Metadata != nil { + if err := mw.WriteField("metadata", *req.Metadata); err != nil { + return nil, err + } + } + + if err := mw.Close(); err != nil { + return nil, err + } + + return mw, nil +} + +func NewSegmentAnything2MultipartWriter(w io.Writer, req GenSegmentAnything2MultipartRequestBody) (*multipart.Writer, error) { + mw := multipart.NewWriter(w) + 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) + } + + // Handle input fields. + if req.ModelId != nil { + if err := mw.WriteField("model_id", *req.ModelId); err != nil { + return nil, err + } + } + if req.PointCoords != nil { + if err := mw.WriteField("point_coords", *req.PointCoords); err != nil { + return nil, err + } + } + if req.PointLabels != nil { + if err := mw.WriteField("point_labels", *req.PointLabels); err != nil { + return nil, err + } + } + if req.Box != nil { + if err := mw.WriteField("box", *req.Box); err != nil { + return nil, err + } + } + if req.MaskInput != nil { + if err := mw.WriteField("mask_input", *req.MaskInput); err != nil { + return nil, err + } + } + if req.MultimaskOutput != nil { + if err := mw.WriteField("multimask_output", strconv.FormatBool(*req.MultimaskOutput)); err != nil { + return nil, err + } + } + if req.ReturnLogits != nil { + if err := mw.WriteField("return_logits", strconv.FormatBool(*req.ReturnLogits)); err != nil { + return nil, err + } + } + if req.NormalizeCoords != nil { + if err := mw.WriteField("normalize_coords", strconv.FormatBool(*req.NormalizeCoords)); err != nil { + return nil, err + } + } + + if err := mw.Close(); err != nil { + return nil, err + } + + return mw, nil +} + +func NewImageToTextMultipartWriter(w io.Writer, req GenImageToTextMultipartRequestBody) (*multipart.Writer, error) { + mw := multipart.NewWriter(w) + 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 req.Prompt != nil { + if err := mw.WriteField("prompt", *req.Prompt); err != nil { + return nil, err + } + } + if req.ModelId != nil { + if err := mw.WriteField("model_id", *req.ModelId); err != nil { + return nil, err + } + } + + if err := mw.Close(); err != nil { + return nil, err + } + + return mw, nil +} diff --git a/ai/worker/runner.gen.go b/ai/worker/runner.gen.go new file mode 100644 index 0000000000..eddaff9d16 --- /dev/null +++ b/ai/worker/runner.gen.go @@ -0,0 +1,3164 @@ +// Package worker provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.2.0 DO NOT EDIT. +package worker + +import ( + "bytes" + "compress/gzip" + "context" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "path" + "strings" + + "github.com/getkin/kin-openapi/openapi3" + "github.com/go-chi/chi/v5" + "github.com/oapi-codegen/runtime" + openapi_types "github.com/oapi-codegen/runtime/types" +) + +const ( + HTTPBearerScopes = "HTTPBearer.Scopes" +) + +// Defines values for HealthCheckStatus. +const ( + ERROR HealthCheckStatus = "ERROR" + IDLE HealthCheckStatus = "IDLE" + OK HealthCheckStatus = "OK" +) + +// APIError API error response model. +type APIError struct { + // Msg The error message. + Msg string `json:"msg"` +} + +// AudioResponse Response model for audio generation. +type AudioResponse struct { + // Audio The generated audio. + Audio MediaURL `json:"audio"` +} + +// BodyGenAudioToText defines model for Body_genAudioToText. +type BodyGenAudioToText struct { + // Audio Uploaded audio file to be transcribed. + Audio openapi_types.File `json:"audio"` + + // Metadata Additional job information to be passed to the pipeline. + Metadata *string `json:"metadata,omitempty"` + + // ModelId Hugging Face model ID used for transcription. + ModelId *string `json:"model_id,omitempty"` + + // ReturnTimestamps Return timestamps for the transcribed text. Supported values: 'sentence', 'word', or a string boolean ('true' or 'false'). Default is 'true' ('sentence'). 'false' means no timestamps. 'word' means word-based timestamps. + ReturnTimestamps *string `json:"return_timestamps,omitempty"` +} + +// BodyGenImageToImage defines model for Body_genImageToImage. +type BodyGenImageToImage struct { + // GuidanceScale Encourages model to generate images closely linked to the text prompt (higher values may reduce image quality). + GuidanceScale *float32 `json:"guidance_scale,omitempty"` + + // Image Uploaded image to modify with the pipeline. + Image openapi_types.File `json:"image"` + + // ImageGuidanceScale Degree to which the generated image is pushed towards the initial image. + ImageGuidanceScale *float32 `json:"image_guidance_scale,omitempty"` + + // Loras A LoRA (Low-Rank Adaptation) model and its corresponding weight for image generation. Example: { "latent-consistency/lcm-lora-sdxl": 1.0, "nerijs/pixel-art-xl": 1.2}. + Loras *string `json:"loras,omitempty"` + + // ModelId Hugging Face model ID used for image generation. + ModelId *string `json:"model_id,omitempty"` + + // NegativePrompt Text prompt(s) to guide what to exclude from image generation. Ignored if guidance_scale < 1. + NegativePrompt *string `json:"negative_prompt,omitempty"` + + // NumImagesPerPrompt Number of images to generate per prompt. + NumImagesPerPrompt *int `json:"num_images_per_prompt,omitempty"` + + // NumInferenceSteps Number of denoising steps. More steps usually lead to higher quality images but slower inference. Modulated by strength. + NumInferenceSteps *int `json:"num_inference_steps,omitempty"` + + // Prompt Text prompt(s) to guide image generation. + Prompt string `json:"prompt"` + + // SafetyCheck Perform a safety check to estimate if generated images could be offensive or harmful. + SafetyCheck *bool `json:"safety_check,omitempty"` + + // Seed Seed for random number generation. + Seed *int `json:"seed,omitempty"` + + // Strength Degree of transformation applied to the reference image (0 to 1). + Strength *float32 `json:"strength,omitempty"` +} + +// BodyGenImageToText defines model for Body_genImageToText. +type BodyGenImageToText struct { + // Image Uploaded image to transform with the pipeline. + Image openapi_types.File `json:"image"` + + // ModelId Hugging Face model ID used for transformation. + ModelId *string `json:"model_id,omitempty"` + + // Prompt Text prompt(s) to guide transformation. + Prompt *string `json:"prompt,omitempty"` +} + +// BodyGenImageToVideo defines model for Body_genImageToVideo. +type BodyGenImageToVideo struct { + // Fps The frames per second of the generated video. + Fps *int `json:"fps,omitempty"` + + // Height The height in pixels of the generated video. + Height *int `json:"height,omitempty"` + + // Image Uploaded image to generate a video from. + Image openapi_types.File `json:"image"` + + // ModelId Hugging Face model ID used for video generation. + ModelId *string `json:"model_id,omitempty"` + + // MotionBucketId Used for conditioning the amount of motion for the generation. The higher the number the more motion will be in the video. + MotionBucketId *int `json:"motion_bucket_id,omitempty"` + + // NoiseAugStrength Amount of noise added to the conditioning image. Higher values reduce resemblance to the conditioning image and increase motion. + NoiseAugStrength *float32 `json:"noise_aug_strength,omitempty"` + + // NumInferenceSteps Number of denoising steps. More steps usually lead to higher quality images but slower inference. Modulated by strength. + NumInferenceSteps *int `json:"num_inference_steps,omitempty"` + + // SafetyCheck Perform a safety check to estimate if generated images could be offensive or harmful. + SafetyCheck *bool `json:"safety_check,omitempty"` + + // Seed Seed for random number generation. + Seed *int `json:"seed,omitempty"` + + // Width The width in pixels of the generated video. + Width *int `json:"width,omitempty"` +} + +// BodyGenSegmentAnything2 defines model for Body_genSegmentAnything2. +type BodyGenSegmentAnything2 struct { + // Box A length 4 array given as a box prompt to the model, in XYXY format. + Box *string `json:"box,omitempty"` + + // Image Image to segment. + Image openapi_types.File `json:"image"` + + // MaskInput A low-resolution mask input to the model, typically from a previous prediction iteration, with the form 1xHxW (H=W=256 for SAM). + MaskInput *string `json:"mask_input,omitempty"` + + // ModelId Hugging Face model ID used for image generation. + ModelId *string `json:"model_id,omitempty"` + + // MultimaskOutput If true, the model will return three masks for ambiguous input prompts, often producing better masks than a single prediction. + MultimaskOutput *bool `json:"multimask_output,omitempty"` + + // NormalizeCoords If true, the point coordinates will be normalized to the range [0,1], with point_coords expected to be with respect to image dimensions. + NormalizeCoords *bool `json:"normalize_coords,omitempty"` + + // PointCoords Nx2 array of point prompts to the model, where each point is in (X,Y) in pixels. + PointCoords *string `json:"point_coords,omitempty"` + + // PointLabels Labels for the point prompts, where 1 indicates a foreground point and 0 indicates a background point. + PointLabels *string `json:"point_labels,omitempty"` + + // ReturnLogits If true, returns un-thresholded mask logits instead of a binary mask. + ReturnLogits *bool `json:"return_logits,omitempty"` +} + +// BodyGenUpscale defines model for Body_genUpscale. +type BodyGenUpscale struct { + // Image Uploaded image to modify with the pipeline. + Image openapi_types.File `json:"image"` + + // ModelId Hugging Face model ID used for upscaled image generation. + ModelId *string `json:"model_id,omitempty"` + + // NumInferenceSteps Number of denoising steps. More steps usually lead to higher quality images but slower inference. Modulated by strength. + NumInferenceSteps *int `json:"num_inference_steps,omitempty"` + + // Prompt Text prompt(s) to guide upscaled image generation. + Prompt string `json:"prompt"` + + // SafetyCheck Perform a safety check to estimate if generated images could be offensive or harmful. + SafetyCheck *bool `json:"safety_check,omitempty"` + + // Seed Seed for random number generation. + Seed *int `json:"seed,omitempty"` +} + +// Chunk A chunk of text with a timestamp. +type Chunk struct { + // Text The text of the chunk. + Text string `json:"text"` + + // Timestamp The timestamp of the chunk. + Timestamp []interface{} `json:"timestamp"` +} + +// GPUComputeInfo Model for detailed GPU compute information. +type GPUComputeInfo struct { + Id string `json:"id"` + Major int `json:"major"` + MemoryFree int `json:"memory_free"` + MemoryTotal int `json:"memory_total"` + Minor int `json:"minor"` + Name string `json:"name"` +} + +// GPUUtilizationInfo Model for GPU utilization statistics. +type GPUUtilizationInfo struct { + Id string `json:"id"` + MemoryFree int `json:"memory_free"` + MemoryTotal int `json:"memory_total"` + Name string `json:"name"` + UtilizationCompute int `json:"utilization_compute"` + UtilizationMemory int `json:"utilization_memory"` +} + +// HTTPError HTTP error response model. +type HTTPError struct { + // Detail Detailed error information. + Detail APIError `json:"detail"` +} + +// HTTPValidationError defines model for HTTPValidationError. +type HTTPValidationError struct { + Detail *[]ValidationError `json:"detail,omitempty"` +} + +// HardwareInformation Response model for GPU information. +type HardwareInformation struct { + GpuInfo map[string]GPUComputeInfo `json:"gpu_info"` + ModelId string `json:"model_id"` + Pipeline string `json:"pipeline"` +} + +// HardwareStats Response model for real-time GPU statistics. +type HardwareStats struct { + GpuStats map[string]GPUUtilizationInfo `json:"gpu_stats"` + ModelId string `json:"model_id"` + Pipeline string `json:"pipeline"` +} + +// HealthCheck defines model for HealthCheck. +type HealthCheck struct { + // Status The health status of the pipeline + Status HealthCheckStatus `json:"status"` +} + +// HealthCheckStatus The health status of the pipeline +type HealthCheckStatus string + +// ImageResponse Response model for image generation. +type ImageResponse struct { + // Images The generated images. + Images []Media `json:"images"` +} + +// ImageToTextResponse Response model for text generation. +type ImageToTextResponse struct { + // Text The generated text. + Text string `json:"text"` +} + +// LLMChoice defines model for LLMChoice. +type LLMChoice struct { + Delta *LLMMessage `json:"delta,omitempty"` + FinishReason *string `json:"finish_reason,omitempty"` + Index int `json:"index"` + Message *LLMMessage `json:"message,omitempty"` +} + +// LLMMessage defines model for LLMMessage. +type LLMMessage struct { + Content string `json:"content"` + Role string `json:"role"` +} + +// LLMRequest defines model for LLMRequest. +type LLMRequest struct { + MaxTokens *int `json:"max_tokens,omitempty"` + Messages []LLMMessage `json:"messages"` + Model *string `json:"model,omitempty"` + Stream *bool `json:"stream,omitempty"` + Temperature *float32 `json:"temperature,omitempty"` + TopK *int `json:"top_k,omitempty"` + TopP *float32 `json:"top_p,omitempty"` +} + +// LLMResponse defines model for LLMResponse. +type LLMResponse struct { + Choices []LLMChoice `json:"choices"` + Created int `json:"created"` + Id string `json:"id"` + Model string `json:"model"` + Usage LLMTokenUsage `json:"usage"` +} + +// LLMTokenUsage defines model for LLMTokenUsage. +type LLMTokenUsage struct { + CompletionTokens int `json:"completion_tokens"` + PromptTokens int `json:"prompt_tokens"` + TotalTokens int `json:"total_tokens"` +} + +// LiveVideoToVideoParams defines model for LiveVideoToVideoParams. +type LiveVideoToVideoParams struct { + // ControlUrl URL for subscribing via Trickle protocol for updates in the live video-to-video generation params. + ControlUrl *string `json:"control_url,omitempty"` + + // EventsUrl URL for publishing events via Trickle protocol for pipeline status and logs. + EventsUrl *string `json:"events_url,omitempty"` + + // ModelId Name of the pipeline to run in the live video to video job. Notice that this is named model_id for consistency with other routes, but it does not refer to a Hugging Face model ID. The exact model(s) depends on the pipeline implementation and might be configurable via the `params` argument. + ModelId *string `json:"model_id,omitempty"` + + // Params Initial parameters for the pipeline. + Params *map[string]interface{} `json:"params,omitempty"` + + // PublishUrl Destination URL of the outgoing stream to publish. + PublishUrl string `json:"publish_url"` + + // SubscribeUrl Source URL of the incoming stream to subscribe to. + SubscribeUrl string `json:"subscribe_url"` +} + +// LiveVideoToVideoResponse Response model for live video-to-video generation. +type LiveVideoToVideoResponse struct { + // ControlUrl URL for updating the live video-to-video generation + ControlUrl *string `json:"control_url,omitempty"` + + // EventsUrl URL for subscribing to events for pipeline status and logs + EventsUrl *string `json:"events_url,omitempty"` + + // PublishUrl Destination URL of the outgoing stream to publish to + PublishUrl string `json:"publish_url"` + + // SubscribeUrl Source URL of the incoming stream to subscribe to + SubscribeUrl string `json:"subscribe_url"` +} + +// MasksResponse Response model for object segmentation. +type MasksResponse struct { + // Logits The raw, unnormalized predictions (logits) for the masks. + Logits string `json:"logits"` + + // Masks The generated masks. + Masks string `json:"masks"` + + // Scores The model's confidence scores for each generated mask. + Scores string `json:"scores"` +} + +// Media A media object containing information about the generated media. +type Media struct { + // Nsfw Whether the media was flagged as NSFW. + Nsfw bool `json:"nsfw"` + + // Seed The seed used to generate the media. + Seed int `json:"seed"` + + // Url The URL where the media can be accessed. + Url string `json:"url"` +} + +// MediaURL A URL from which media can be accessed. +type MediaURL struct { + // Url The URL where the media can be accessed. + Url string `json:"url"` +} + +// TextResponse Response model for text generation. +type TextResponse struct { + // Chunks The generated text chunks. + Chunks []Chunk `json:"chunks"` + + // Text The generated text. + Text string `json:"text"` +} + +// TextToImageParams defines model for TextToImageParams. +type TextToImageParams struct { + // GuidanceScale Encourages model to generate images closely linked to the text prompt (higher values may reduce image quality). + GuidanceScale *float32 `json:"guidance_scale,omitempty"` + + // Height The height in pixels of the generated image. + Height *int `json:"height,omitempty"` + + // Loras A LoRA (Low-Rank Adaptation) model and its corresponding weight for image generation. Example: { "latent-consistency/lcm-lora-sdxl": 1.0, "nerijs/pixel-art-xl": 1.2}. + Loras *string `json:"loras,omitempty"` + + // ModelId Hugging Face model ID used for image generation. + ModelId *string `json:"model_id,omitempty"` + + // NegativePrompt Text prompt(s) to guide what to exclude from image generation. Ignored if guidance_scale < 1. + NegativePrompt *string `json:"negative_prompt,omitempty"` + + // NumImagesPerPrompt Number of images to generate per prompt. + NumImagesPerPrompt *int `json:"num_images_per_prompt,omitempty"` + + // NumInferenceSteps Number of denoising steps. More steps usually lead to higher quality images but slower inference. Modulated by strength. + NumInferenceSteps *int `json:"num_inference_steps,omitempty"` + + // Prompt Text prompt(s) to guide image generation. Separate multiple prompts with '|' if supported by the model. + Prompt string `json:"prompt"` + + // SafetyCheck Perform a safety check to estimate if generated images could be offensive or harmful. + SafetyCheck *bool `json:"safety_check,omitempty"` + + // Seed Seed for random number generation. + Seed *int `json:"seed,omitempty"` + + // Width The width in pixels of the generated image. + Width *int `json:"width,omitempty"` +} + +// TextToSpeechParams defines model for TextToSpeechParams. +type TextToSpeechParams struct { + // Description Description of speaker to steer text to speech generation. + Description *string `json:"description,omitempty"` + + // ModelId Hugging Face model ID used for text to speech generation. + ModelId *string `json:"model_id,omitempty"` + + // Text Text input for speech generation. + Text *string `json:"text,omitempty"` +} + +// ValidationError defines model for ValidationError. +type ValidationError struct { + Loc []ValidationError_Loc_Item `json:"loc"` + Msg string `json:"msg"` + Type string `json:"type"` +} + +// ValidationErrorLoc0 defines model for . +type ValidationErrorLoc0 = string + +// ValidationErrorLoc1 defines model for . +type ValidationErrorLoc1 = int + +// ValidationError_Loc_Item defines model for ValidationError.loc.Item. +type ValidationError_Loc_Item struct { + union json.RawMessage +} + +// VideoResponse Response model for video generation. +type VideoResponse struct { + // Frames The generated video frames. + Frames [][]Media `json:"frames"` +} + +// GenAudioToTextMultipartRequestBody defines body for GenAudioToText for multipart/form-data ContentType. +type GenAudioToTextMultipartRequestBody = BodyGenAudioToText + +// GenImageToImageMultipartRequestBody defines body for GenImageToImage for multipart/form-data ContentType. +type GenImageToImageMultipartRequestBody = BodyGenImageToImage + +// GenImageToTextMultipartRequestBody defines body for GenImageToText for multipart/form-data ContentType. +type GenImageToTextMultipartRequestBody = BodyGenImageToText + +// GenImageToVideoMultipartRequestBody defines body for GenImageToVideo for multipart/form-data ContentType. +type GenImageToVideoMultipartRequestBody = BodyGenImageToVideo + +// GenLiveVideoToVideoJSONRequestBody defines body for GenLiveVideoToVideo for application/json ContentType. +type GenLiveVideoToVideoJSONRequestBody = LiveVideoToVideoParams + +// GenLLMJSONRequestBody defines body for GenLLM for application/json ContentType. +type GenLLMJSONRequestBody = LLMRequest + +// GenSegmentAnything2MultipartRequestBody defines body for GenSegmentAnything2 for multipart/form-data ContentType. +type GenSegmentAnything2MultipartRequestBody = BodyGenSegmentAnything2 + +// GenTextToImageJSONRequestBody defines body for GenTextToImage for application/json ContentType. +type GenTextToImageJSONRequestBody = TextToImageParams + +// GenTextToSpeechJSONRequestBody defines body for GenTextToSpeech for application/json ContentType. +type GenTextToSpeechJSONRequestBody = TextToSpeechParams + +// GenUpscaleMultipartRequestBody defines body for GenUpscale for multipart/form-data ContentType. +type GenUpscaleMultipartRequestBody = BodyGenUpscale + +// AsValidationErrorLoc0 returns the union data inside the ValidationError_Loc_Item as a ValidationErrorLoc0 +func (t ValidationError_Loc_Item) AsValidationErrorLoc0() (ValidationErrorLoc0, error) { + var body ValidationErrorLoc0 + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromValidationErrorLoc0 overwrites any union data inside the ValidationError_Loc_Item as the provided ValidationErrorLoc0 +func (t *ValidationError_Loc_Item) FromValidationErrorLoc0(v ValidationErrorLoc0) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeValidationErrorLoc0 performs a merge with any union data inside the ValidationError_Loc_Item, using the provided ValidationErrorLoc0 +func (t *ValidationError_Loc_Item) MergeValidationErrorLoc0(v ValidationErrorLoc0) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsValidationErrorLoc1 returns the union data inside the ValidationError_Loc_Item as a ValidationErrorLoc1 +func (t ValidationError_Loc_Item) AsValidationErrorLoc1() (ValidationErrorLoc1, error) { + var body ValidationErrorLoc1 + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromValidationErrorLoc1 overwrites any union data inside the ValidationError_Loc_Item as the provided ValidationErrorLoc1 +func (t *ValidationError_Loc_Item) FromValidationErrorLoc1(v ValidationErrorLoc1) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeValidationErrorLoc1 performs a merge with any union data inside the ValidationError_Loc_Item, using the provided ValidationErrorLoc1 +func (t *ValidationError_Loc_Item) MergeValidationErrorLoc1(v ValidationErrorLoc1) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t ValidationError_Loc_Item) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + return b, err +} + +func (t *ValidationError_Loc_Item) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + return err +} + +// RequestEditorFn is the function signature for the RequestEditor callback function +type RequestEditorFn func(ctx context.Context, req *http.Request) error + +// Doer performs HTTP requests. +// +// The standard http.Client implements this interface. +type HttpRequestDoer interface { + Do(req *http.Request) (*http.Response, error) +} + +// Client which conforms to the OpenAPI3 specification for this service. +type Client struct { + // The endpoint of the server conforming to this interface, with scheme, + // https://api.deepmap.com for example. This can contain a path relative + // to the server, such as https://api.deepmap.com/dev-test, and all the + // paths in the swagger spec will be appended to the server. + Server string + + // Doer for performing requests, typically a *http.Client with any + // customized settings, such as certificate chains. + Client HttpRequestDoer + + // A list of callbacks for modifying requests which are generated before sending over + // the network. + RequestEditors []RequestEditorFn +} + +// ClientOption allows setting custom parameters during construction +type ClientOption func(*Client) error + +// Creates a new Client, with reasonable defaults +func NewClient(server string, opts ...ClientOption) (*Client, error) { + // create a client with sane default values + client := Client{ + Server: server, + } + // mutate client and add all optional params + for _, o := range opts { + if err := o(&client); err != nil { + return nil, err + } + } + // ensure the server URL always has a trailing slash + if !strings.HasSuffix(client.Server, "/") { + client.Server += "/" + } + // create httpClient, if not already present + if client.Client == nil { + client.Client = &http.Client{} + } + return &client, nil +} + +// WithHTTPClient allows overriding the default Doer, which is +// automatically created using http.Client. This is useful for tests. +func WithHTTPClient(doer HttpRequestDoer) ClientOption { + return func(c *Client) error { + c.Client = doer + return nil + } +} + +// WithRequestEditorFn allows setting up a callback function, which will be +// called right before sending the request. This can be used to mutate the request. +func WithRequestEditorFn(fn RequestEditorFn) ClientOption { + return func(c *Client) error { + c.RequestEditors = append(c.RequestEditors, fn) + return nil + } +} + +// The interface specification for the client above. +type ClientInterface interface { + // GenAudioToTextWithBody request with any body + GenAudioToTextWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // HardwareInfo request + HardwareInfo(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // HardwareStats request + HardwareStats(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // Health request + Health(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GenImageToImageWithBody request with any body + GenImageToImageWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GenImageToTextWithBody request with any body + GenImageToTextWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GenImageToVideoWithBody request with any body + GenImageToVideoWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GenLiveVideoToVideoWithBody request with any body + GenLiveVideoToVideoWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + GenLiveVideoToVideo(ctx context.Context, body GenLiveVideoToVideoJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GenLLMWithBody request with any body + GenLLMWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + GenLLM(ctx context.Context, body GenLLMJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GenSegmentAnything2WithBody request with any body + GenSegmentAnything2WithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GenTextToImageWithBody request with any body + GenTextToImageWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + GenTextToImage(ctx context.Context, body GenTextToImageJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GenTextToSpeechWithBody request with any body + GenTextToSpeechWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + GenTextToSpeech(ctx context.Context, body GenTextToSpeechJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GenUpscaleWithBody request with any body + GenUpscaleWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) +} + +func (c *Client) GenAudioToTextWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGenAudioToTextRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) HardwareInfo(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewHardwareInfoRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) HardwareStats(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewHardwareStatsRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) Health(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewHealthRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GenImageToImageWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGenImageToImageRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GenImageToTextWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGenImageToTextRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GenImageToVideoWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGenImageToVideoRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GenLiveVideoToVideoWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGenLiveVideoToVideoRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GenLiveVideoToVideo(ctx context.Context, body GenLiveVideoToVideoJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGenLiveVideoToVideoRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GenLLMWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGenLLMRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GenLLM(ctx context.Context, body GenLLMJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGenLLMRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GenSegmentAnything2WithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGenSegmentAnything2RequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GenTextToImageWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGenTextToImageRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GenTextToImage(ctx context.Context, body GenTextToImageJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGenTextToImageRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GenTextToSpeechWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGenTextToSpeechRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GenTextToSpeech(ctx context.Context, body GenTextToSpeechJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGenTextToSpeechRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GenUpscaleWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGenUpscaleRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// NewGenAudioToTextRequestWithBody generates requests for GenAudioToText with any type of body +func NewGenAudioToTextRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/audio-to-text") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewHardwareInfoRequest generates requests for HardwareInfo +func NewHardwareInfoRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/hardware/info") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewHardwareStatsRequest generates requests for HardwareStats +func NewHardwareStatsRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/hardware/stats") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewHealthRequest generates requests for Health +func NewHealthRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/health") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGenImageToImageRequestWithBody generates requests for GenImageToImage with any type of body +func NewGenImageToImageRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/image-to-image") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGenImageToTextRequestWithBody generates requests for GenImageToText with any type of body +func NewGenImageToTextRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/image-to-text") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGenImageToVideoRequestWithBody generates requests for GenImageToVideo with any type of body +func NewGenImageToVideoRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/image-to-video") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGenLiveVideoToVideoRequest calls the generic GenLiveVideoToVideo builder with application/json body +func NewGenLiveVideoToVideoRequest(server string, body GenLiveVideoToVideoJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewGenLiveVideoToVideoRequestWithBody(server, "application/json", bodyReader) +} + +// NewGenLiveVideoToVideoRequestWithBody generates requests for GenLiveVideoToVideo with any type of body +func NewGenLiveVideoToVideoRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/live-video-to-video") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGenLLMRequest calls the generic GenLLM builder with application/json body +func NewGenLLMRequest(server string, body GenLLMJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewGenLLMRequestWithBody(server, "application/json", bodyReader) +} + +// NewGenLLMRequestWithBody generates requests for GenLLM with any type of body +func NewGenLLMRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/llm") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGenSegmentAnything2RequestWithBody generates requests for GenSegmentAnything2 with any type of body +func NewGenSegmentAnything2RequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/segment-anything-2") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGenTextToImageRequest calls the generic GenTextToImage builder with application/json body +func NewGenTextToImageRequest(server string, body GenTextToImageJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewGenTextToImageRequestWithBody(server, "application/json", bodyReader) +} + +// NewGenTextToImageRequestWithBody generates requests for GenTextToImage with any type of body +func NewGenTextToImageRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/text-to-image") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGenTextToSpeechRequest calls the generic GenTextToSpeech builder with application/json body +func NewGenTextToSpeechRequest(server string, body GenTextToSpeechJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewGenTextToSpeechRequestWithBody(server, "application/json", bodyReader) +} + +// NewGenTextToSpeechRequestWithBody generates requests for GenTextToSpeech with any type of body +func NewGenTextToSpeechRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/text-to-speech") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGenUpscaleRequestWithBody generates requests for GenUpscale with any type of body +func NewGenUpscaleRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/upscale") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { + for _, r := range c.RequestEditors { + if err := r(ctx, req); err != nil { + return err + } + } + for _, r := range additionalEditors { + if err := r(ctx, req); err != nil { + return err + } + } + return nil +} + +// ClientWithResponses builds on ClientInterface to offer response payloads +type ClientWithResponses struct { + ClientInterface +} + +// NewClientWithResponses creates a new ClientWithResponses, which wraps +// Client with return type handling +func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) { + client, err := NewClient(server, opts...) + if err != nil { + return nil, err + } + return &ClientWithResponses{client}, nil +} + +// WithBaseURL overrides the baseURL. +func WithBaseURL(baseURL string) ClientOption { + return func(c *Client) error { + newBaseURL, err := url.Parse(baseURL) + if err != nil { + return err + } + c.Server = newBaseURL.String() + return nil + } +} + +// ClientWithResponsesInterface is the interface specification for the client with responses above. +type ClientWithResponsesInterface interface { + // GenAudioToTextWithBodyWithResponse request with any body + GenAudioToTextWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GenAudioToTextResponse, error) + + // HardwareInfoWithResponse request + HardwareInfoWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*HardwareInfoResponse, error) + + // HardwareStatsWithResponse request + HardwareStatsWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*HardwareStatsResponse, error) + + // HealthWithResponse request + HealthWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*HealthResponse, error) + + // GenImageToImageWithBodyWithResponse request with any body + GenImageToImageWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GenImageToImageResponse, error) + + // GenImageToTextWithBodyWithResponse request with any body + GenImageToTextWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GenImageToTextResponse, error) + + // GenImageToVideoWithBodyWithResponse request with any body + GenImageToVideoWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GenImageToVideoResponse, error) + + // GenLiveVideoToVideoWithBodyWithResponse request with any body + GenLiveVideoToVideoWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GenLiveVideoToVideoResponse, error) + + GenLiveVideoToVideoWithResponse(ctx context.Context, body GenLiveVideoToVideoJSONRequestBody, reqEditors ...RequestEditorFn) (*GenLiveVideoToVideoResponse, error) + + // GenLLMWithBodyWithResponse request with any body + GenLLMWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GenLLMResponse, error) + + GenLLMWithResponse(ctx context.Context, body GenLLMJSONRequestBody, reqEditors ...RequestEditorFn) (*GenLLMResponse, error) + + // GenSegmentAnything2WithBodyWithResponse request with any body + GenSegmentAnything2WithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GenSegmentAnything2Response, error) + + // GenTextToImageWithBodyWithResponse request with any body + GenTextToImageWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GenTextToImageResponse, error) + + GenTextToImageWithResponse(ctx context.Context, body GenTextToImageJSONRequestBody, reqEditors ...RequestEditorFn) (*GenTextToImageResponse, error) + + // GenTextToSpeechWithBodyWithResponse request with any body + GenTextToSpeechWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GenTextToSpeechResponse, error) + + GenTextToSpeechWithResponse(ctx context.Context, body GenTextToSpeechJSONRequestBody, reqEditors ...RequestEditorFn) (*GenTextToSpeechResponse, error) + + // GenUpscaleWithBodyWithResponse request with any body + GenUpscaleWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GenUpscaleResponse, error) +} + +type GenAudioToTextResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *TextResponse + JSON400 *HTTPError + JSON401 *HTTPError + JSON413 *HTTPError + JSON415 *HTTPError + JSON422 *HTTPValidationError + JSON500 *HTTPError +} + +// Status returns HTTPResponse.Status +func (r GenAudioToTextResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GenAudioToTextResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type HardwareInfoResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *HardwareInformation +} + +// Status returns HTTPResponse.Status +func (r HardwareInfoResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r HardwareInfoResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type HardwareStatsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *HardwareStats +} + +// Status returns HTTPResponse.Status +func (r HardwareStatsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r HardwareStatsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type HealthResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *HealthCheck +} + +// Status returns HTTPResponse.Status +func (r HealthResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r HealthResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type GenImageToImageResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *ImageResponse + JSON400 *HTTPError + JSON401 *HTTPError + JSON422 *HTTPValidationError + JSON500 *HTTPError +} + +// Status returns HTTPResponse.Status +func (r GenImageToImageResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GenImageToImageResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type GenImageToTextResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *ImageToTextResponse + JSON400 *HTTPError + JSON401 *HTTPError + JSON413 *HTTPError + JSON422 *HTTPValidationError + JSON500 *HTTPError +} + +// Status returns HTTPResponse.Status +func (r GenImageToTextResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GenImageToTextResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type GenImageToVideoResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *VideoResponse + JSON400 *HTTPError + JSON401 *HTTPError + JSON422 *HTTPValidationError + JSON500 *HTTPError +} + +// Status returns HTTPResponse.Status +func (r GenImageToVideoResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GenImageToVideoResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type GenLiveVideoToVideoResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *LiveVideoToVideoResponse + JSON400 *HTTPError + JSON401 *HTTPError + JSON422 *HTTPValidationError + JSON500 *HTTPError +} + +// Status returns HTTPResponse.Status +func (r GenLiveVideoToVideoResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GenLiveVideoToVideoResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type GenLLMResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *LLMResponse + JSON400 *HTTPError + JSON401 *HTTPError + JSON422 *HTTPValidationError + JSON500 *HTTPError +} + +// Status returns HTTPResponse.Status +func (r GenLLMResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GenLLMResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type GenSegmentAnything2Response struct { + Body []byte + HTTPResponse *http.Response + JSON200 *MasksResponse + JSON400 *HTTPError + JSON401 *HTTPError + JSON422 *HTTPValidationError + JSON500 *HTTPError +} + +// Status returns HTTPResponse.Status +func (r GenSegmentAnything2Response) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GenSegmentAnything2Response) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type GenTextToImageResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *ImageResponse + JSON400 *HTTPError + JSON401 *HTTPError + JSON422 *HTTPValidationError + JSON500 *HTTPError +} + +// Status returns HTTPResponse.Status +func (r GenTextToImageResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GenTextToImageResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type GenTextToSpeechResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *AudioResponse + JSON400 *HTTPError + JSON401 *HTTPError + JSON422 *HTTPValidationError + JSON500 *HTTPError +} + +// Status returns HTTPResponse.Status +func (r GenTextToSpeechResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GenTextToSpeechResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type GenUpscaleResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *ImageResponse + JSON400 *HTTPError + JSON401 *HTTPError + JSON422 *HTTPValidationError + JSON500 *HTTPError +} + +// Status returns HTTPResponse.Status +func (r GenUpscaleResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GenUpscaleResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// GenAudioToTextWithBodyWithResponse request with arbitrary body returning *GenAudioToTextResponse +func (c *ClientWithResponses) GenAudioToTextWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GenAudioToTextResponse, error) { + rsp, err := c.GenAudioToTextWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGenAudioToTextResponse(rsp) +} + +// HardwareInfoWithResponse request returning *HardwareInfoResponse +func (c *ClientWithResponses) HardwareInfoWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*HardwareInfoResponse, error) { + rsp, err := c.HardwareInfo(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseHardwareInfoResponse(rsp) +} + +// HardwareStatsWithResponse request returning *HardwareStatsResponse +func (c *ClientWithResponses) HardwareStatsWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*HardwareStatsResponse, error) { + rsp, err := c.HardwareStats(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseHardwareStatsResponse(rsp) +} + +// HealthWithResponse request returning *HealthResponse +func (c *ClientWithResponses) HealthWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*HealthResponse, error) { + rsp, err := c.Health(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseHealthResponse(rsp) +} + +// GenImageToImageWithBodyWithResponse request with arbitrary body returning *GenImageToImageResponse +func (c *ClientWithResponses) GenImageToImageWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GenImageToImageResponse, error) { + rsp, err := c.GenImageToImageWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGenImageToImageResponse(rsp) +} + +// GenImageToTextWithBodyWithResponse request with arbitrary body returning *GenImageToTextResponse +func (c *ClientWithResponses) GenImageToTextWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GenImageToTextResponse, error) { + rsp, err := c.GenImageToTextWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGenImageToTextResponse(rsp) +} + +// GenImageToVideoWithBodyWithResponse request with arbitrary body returning *GenImageToVideoResponse +func (c *ClientWithResponses) GenImageToVideoWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GenImageToVideoResponse, error) { + rsp, err := c.GenImageToVideoWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGenImageToVideoResponse(rsp) +} + +// GenLiveVideoToVideoWithBodyWithResponse request with arbitrary body returning *GenLiveVideoToVideoResponse +func (c *ClientWithResponses) GenLiveVideoToVideoWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GenLiveVideoToVideoResponse, error) { + rsp, err := c.GenLiveVideoToVideoWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGenLiveVideoToVideoResponse(rsp) +} + +func (c *ClientWithResponses) GenLiveVideoToVideoWithResponse(ctx context.Context, body GenLiveVideoToVideoJSONRequestBody, reqEditors ...RequestEditorFn) (*GenLiveVideoToVideoResponse, error) { + rsp, err := c.GenLiveVideoToVideo(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGenLiveVideoToVideoResponse(rsp) +} + +// GenLLMWithBodyWithResponse request with arbitrary body returning *GenLLMResponse +func (c *ClientWithResponses) GenLLMWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GenLLMResponse, error) { + rsp, err := c.GenLLMWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGenLLMResponse(rsp) +} + +func (c *ClientWithResponses) GenLLMWithResponse(ctx context.Context, body GenLLMJSONRequestBody, reqEditors ...RequestEditorFn) (*GenLLMResponse, error) { + rsp, err := c.GenLLM(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGenLLMResponse(rsp) +} + +// GenSegmentAnything2WithBodyWithResponse request with arbitrary body returning *GenSegmentAnything2Response +func (c *ClientWithResponses) GenSegmentAnything2WithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GenSegmentAnything2Response, error) { + rsp, err := c.GenSegmentAnything2WithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGenSegmentAnything2Response(rsp) +} + +// GenTextToImageWithBodyWithResponse request with arbitrary body returning *GenTextToImageResponse +func (c *ClientWithResponses) GenTextToImageWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GenTextToImageResponse, error) { + rsp, err := c.GenTextToImageWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGenTextToImageResponse(rsp) +} + +func (c *ClientWithResponses) GenTextToImageWithResponse(ctx context.Context, body GenTextToImageJSONRequestBody, reqEditors ...RequestEditorFn) (*GenTextToImageResponse, error) { + rsp, err := c.GenTextToImage(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGenTextToImageResponse(rsp) +} + +// GenTextToSpeechWithBodyWithResponse request with arbitrary body returning *GenTextToSpeechResponse +func (c *ClientWithResponses) GenTextToSpeechWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GenTextToSpeechResponse, error) { + rsp, err := c.GenTextToSpeechWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGenTextToSpeechResponse(rsp) +} + +func (c *ClientWithResponses) GenTextToSpeechWithResponse(ctx context.Context, body GenTextToSpeechJSONRequestBody, reqEditors ...RequestEditorFn) (*GenTextToSpeechResponse, error) { + rsp, err := c.GenTextToSpeech(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGenTextToSpeechResponse(rsp) +} + +// GenUpscaleWithBodyWithResponse request with arbitrary body returning *GenUpscaleResponse +func (c *ClientWithResponses) GenUpscaleWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GenUpscaleResponse, error) { + rsp, err := c.GenUpscaleWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGenUpscaleResponse(rsp) +} + +// ParseGenAudioToTextResponse parses an HTTP response from a GenAudioToTextWithResponse call +func ParseGenAudioToTextResponse(rsp *http.Response) (*GenAudioToTextResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GenAudioToTextResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TextResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 413: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON413 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 415: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON415 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest HTTPValidationError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON422 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseHardwareInfoResponse parses an HTTP response from a HardwareInfoWithResponse call +func ParseHardwareInfoResponse(rsp *http.Response) (*HardwareInfoResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &HardwareInfoResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest HardwareInformation + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + } + + return response, nil +} + +// ParseHardwareStatsResponse parses an HTTP response from a HardwareStatsWithResponse call +func ParseHardwareStatsResponse(rsp *http.Response) (*HardwareStatsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &HardwareStatsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest HardwareStats + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + } + + return response, nil +} + +// ParseHealthResponse parses an HTTP response from a HealthWithResponse call +func ParseHealthResponse(rsp *http.Response) (*HealthResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &HealthResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest HealthCheck + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + } + + return response, nil +} + +// ParseGenImageToImageResponse parses an HTTP response from a GenImageToImageWithResponse call +func ParseGenImageToImageResponse(rsp *http.Response) (*GenImageToImageResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GenImageToImageResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ImageResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest HTTPValidationError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON422 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGenImageToTextResponse parses an HTTP response from a GenImageToTextWithResponse call +func ParseGenImageToTextResponse(rsp *http.Response) (*GenImageToTextResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GenImageToTextResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ImageToTextResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 413: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON413 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest HTTPValidationError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON422 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGenImageToVideoResponse parses an HTTP response from a GenImageToVideoWithResponse call +func ParseGenImageToVideoResponse(rsp *http.Response) (*GenImageToVideoResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GenImageToVideoResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest VideoResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest HTTPValidationError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON422 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGenLiveVideoToVideoResponse parses an HTTP response from a GenLiveVideoToVideoWithResponse call +func ParseGenLiveVideoToVideoResponse(rsp *http.Response) (*GenLiveVideoToVideoResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GenLiveVideoToVideoResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest LiveVideoToVideoResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest HTTPValidationError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON422 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGenLLMResponse parses an HTTP response from a GenLLMWithResponse call +func ParseGenLLMResponse(rsp *http.Response) (*GenLLMResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GenLLMResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest LLMResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest HTTPValidationError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON422 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGenSegmentAnything2Response parses an HTTP response from a GenSegmentAnything2WithResponse call +func ParseGenSegmentAnything2Response(rsp *http.Response) (*GenSegmentAnything2Response, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GenSegmentAnything2Response{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest MasksResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest HTTPValidationError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON422 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGenTextToImageResponse parses an HTTP response from a GenTextToImageWithResponse call +func ParseGenTextToImageResponse(rsp *http.Response) (*GenTextToImageResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GenTextToImageResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ImageResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest HTTPValidationError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON422 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGenTextToSpeechResponse parses an HTTP response from a GenTextToSpeechWithResponse call +func ParseGenTextToSpeechResponse(rsp *http.Response) (*GenTextToSpeechResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GenTextToSpeechResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest AudioResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest HTTPValidationError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON422 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGenUpscaleResponse parses an HTTP response from a GenUpscaleWithResponse call +func ParseGenUpscaleResponse(rsp *http.Response) (*GenUpscaleResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GenUpscaleResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ImageResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest HTTPValidationError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON422 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest HTTPError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ServerInterface represents all server handlers. +type ServerInterface interface { + // Audio To Text + // (POST /audio-to-text) + GenAudioToText(w http.ResponseWriter, r *http.Request) + // Hardware Info + // (GET /hardware/info) + HardwareInfo(w http.ResponseWriter, r *http.Request) + // Hardware Stats + // (GET /hardware/stats) + HardwareStats(w http.ResponseWriter, r *http.Request) + // Health + // (GET /health) + Health(w http.ResponseWriter, r *http.Request) + // Image To Image + // (POST /image-to-image) + GenImageToImage(w http.ResponseWriter, r *http.Request) + // Image To Text + // (POST /image-to-text) + GenImageToText(w http.ResponseWriter, r *http.Request) + // Image To Video + // (POST /image-to-video) + GenImageToVideo(w http.ResponseWriter, r *http.Request) + // Live Video To Video + // (POST /live-video-to-video) + GenLiveVideoToVideo(w http.ResponseWriter, r *http.Request) + // LLM + // (POST /llm) + GenLLM(w http.ResponseWriter, r *http.Request) + // Segment Anything 2 + // (POST /segment-anything-2) + GenSegmentAnything2(w http.ResponseWriter, r *http.Request) + // Text To Image + // (POST /text-to-image) + GenTextToImage(w http.ResponseWriter, r *http.Request) + // Text To Speech + // (POST /text-to-speech) + GenTextToSpeech(w http.ResponseWriter, r *http.Request) + // Upscale + // (POST /upscale) + GenUpscale(w http.ResponseWriter, r *http.Request) +} + +// Unimplemented server implementation that returns http.StatusNotImplemented for each endpoint. + +type Unimplemented struct{} + +// Audio To Text +// (POST /audio-to-text) +func (_ Unimplemented) GenAudioToText(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Hardware Info +// (GET /hardware/info) +func (_ Unimplemented) HardwareInfo(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Hardware Stats +// (GET /hardware/stats) +func (_ Unimplemented) HardwareStats(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Health +// (GET /health) +func (_ Unimplemented) Health(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Image To Image +// (POST /image-to-image) +func (_ Unimplemented) GenImageToImage(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Image To Text +// (POST /image-to-text) +func (_ Unimplemented) GenImageToText(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Image To Video +// (POST /image-to-video) +func (_ Unimplemented) GenImageToVideo(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Live Video To Video +// (POST /live-video-to-video) +func (_ Unimplemented) GenLiveVideoToVideo(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// LLM +// (POST /llm) +func (_ Unimplemented) GenLLM(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Segment Anything 2 +// (POST /segment-anything-2) +func (_ Unimplemented) GenSegmentAnything2(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Text To Image +// (POST /text-to-image) +func (_ Unimplemented) GenTextToImage(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Text To Speech +// (POST /text-to-speech) +func (_ Unimplemented) GenTextToSpeech(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Upscale +// (POST /upscale) +func (_ Unimplemented) GenUpscale(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// ServerInterfaceWrapper converts contexts to parameters. +type ServerInterfaceWrapper struct { + Handler ServerInterface + HandlerMiddlewares []MiddlewareFunc + ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) +} + +type MiddlewareFunc func(http.Handler) http.Handler + +// GenAudioToText operation middleware +func (siw *ServerInterfaceWrapper) GenAudioToText(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + ctx = context.WithValue(ctx, HTTPBearerScopes, []string{}) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GenAudioToText(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + +// HardwareInfo operation middleware +func (siw *ServerInterfaceWrapper) HardwareInfo(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.HardwareInfo(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + +// HardwareStats operation middleware +func (siw *ServerInterfaceWrapper) HardwareStats(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.HardwareStats(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + +// Health operation middleware +func (siw *ServerInterfaceWrapper) Health(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.Health(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + +// GenImageToImage operation middleware +func (siw *ServerInterfaceWrapper) GenImageToImage(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + ctx = context.WithValue(ctx, HTTPBearerScopes, []string{}) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GenImageToImage(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + +// GenImageToText operation middleware +func (siw *ServerInterfaceWrapper) GenImageToText(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + ctx = context.WithValue(ctx, HTTPBearerScopes, []string{}) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GenImageToText(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + +// GenImageToVideo operation middleware +func (siw *ServerInterfaceWrapper) GenImageToVideo(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + ctx = context.WithValue(ctx, HTTPBearerScopes, []string{}) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GenImageToVideo(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + +// GenLiveVideoToVideo operation middleware +func (siw *ServerInterfaceWrapper) GenLiveVideoToVideo(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + ctx = context.WithValue(ctx, HTTPBearerScopes, []string{}) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GenLiveVideoToVideo(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + +// GenLLM operation middleware +func (siw *ServerInterfaceWrapper) GenLLM(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + ctx = context.WithValue(ctx, HTTPBearerScopes, []string{}) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GenLLM(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + +// GenSegmentAnything2 operation middleware +func (siw *ServerInterfaceWrapper) GenSegmentAnything2(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + ctx = context.WithValue(ctx, HTTPBearerScopes, []string{}) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GenSegmentAnything2(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + +// GenTextToImage operation middleware +func (siw *ServerInterfaceWrapper) GenTextToImage(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + ctx = context.WithValue(ctx, HTTPBearerScopes, []string{}) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GenTextToImage(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + +// GenTextToSpeech operation middleware +func (siw *ServerInterfaceWrapper) GenTextToSpeech(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + ctx = context.WithValue(ctx, HTTPBearerScopes, []string{}) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GenTextToSpeech(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + +// GenUpscale operation middleware +func (siw *ServerInterfaceWrapper) GenUpscale(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + ctx = context.WithValue(ctx, HTTPBearerScopes, []string{}) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GenUpscale(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + +type UnescapedCookieParamError struct { + ParamName string + Err error +} + +func (e *UnescapedCookieParamError) Error() string { + return fmt.Sprintf("error unescaping cookie parameter '%s'", e.ParamName) +} + +func (e *UnescapedCookieParamError) Unwrap() error { + return e.Err +} + +type UnmarshalingParamError struct { + ParamName string + Err error +} + +func (e *UnmarshalingParamError) Error() string { + return fmt.Sprintf("Error unmarshaling parameter %s as JSON: %s", e.ParamName, e.Err.Error()) +} + +func (e *UnmarshalingParamError) Unwrap() error { + return e.Err +} + +type RequiredParamError struct { + ParamName string +} + +func (e *RequiredParamError) Error() string { + return fmt.Sprintf("Query argument %s is required, but not found", e.ParamName) +} + +type RequiredHeaderError struct { + ParamName string + Err error +} + +func (e *RequiredHeaderError) Error() string { + return fmt.Sprintf("Header parameter %s is required, but not found", e.ParamName) +} + +func (e *RequiredHeaderError) Unwrap() error { + return e.Err +} + +type InvalidParamFormatError struct { + ParamName string + Err error +} + +func (e *InvalidParamFormatError) Error() string { + return fmt.Sprintf("Invalid format for parameter %s: %s", e.ParamName, e.Err.Error()) +} + +func (e *InvalidParamFormatError) Unwrap() error { + return e.Err +} + +type TooManyValuesForParamError struct { + ParamName string + Count int +} + +func (e *TooManyValuesForParamError) Error() string { + return fmt.Sprintf("Expected one value for %s, got %d", e.ParamName, e.Count) +} + +// Handler creates http.Handler with routing matching OpenAPI spec. +func Handler(si ServerInterface) http.Handler { + return HandlerWithOptions(si, ChiServerOptions{}) +} + +type ChiServerOptions struct { + BaseURL string + BaseRouter chi.Router + Middlewares []MiddlewareFunc + ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) +} + +// HandlerFromMux creates http.Handler with routing matching OpenAPI spec based on the provided mux. +func HandlerFromMux(si ServerInterface, r chi.Router) http.Handler { + return HandlerWithOptions(si, ChiServerOptions{ + BaseRouter: r, + }) +} + +func HandlerFromMuxWithBaseURL(si ServerInterface, r chi.Router, baseURL string) http.Handler { + return HandlerWithOptions(si, ChiServerOptions{ + BaseURL: baseURL, + BaseRouter: r, + }) +} + +// HandlerWithOptions creates http.Handler with additional options +func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handler { + r := options.BaseRouter + + if r == nil { + r = chi.NewRouter() + } + if options.ErrorHandlerFunc == nil { + options.ErrorHandlerFunc = func(w http.ResponseWriter, r *http.Request, err error) { + http.Error(w, err.Error(), http.StatusBadRequest) + } + } + wrapper := ServerInterfaceWrapper{ + Handler: si, + HandlerMiddlewares: options.Middlewares, + ErrorHandlerFunc: options.ErrorHandlerFunc, + } + + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/audio-to-text", wrapper.GenAudioToText) + }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/hardware/info", wrapper.HardwareInfo) + }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/hardware/stats", wrapper.HardwareStats) + }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/health", wrapper.Health) + }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/image-to-image", wrapper.GenImageToImage) + }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/image-to-text", wrapper.GenImageToText) + }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/image-to-video", wrapper.GenImageToVideo) + }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/live-video-to-video", wrapper.GenLiveVideoToVideo) + }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/llm", wrapper.GenLLM) + }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/segment-anything-2", wrapper.GenSegmentAnything2) + }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/text-to-image", wrapper.GenTextToImage) + }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/text-to-speech", wrapper.GenTextToSpeech) + }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/upscale", wrapper.GenUpscale) + }) + + return r +} + +// Base64 encoded, gzipped, json marshaled Swagger object +var swaggerSpec = []string{ + + "H4sIAAAAAAAC/+xde2/ctpb/KoR2ASfAjF9t2oWB+4eTpolx7dTw47ZFa8zlSGc0jCVSJSnb06y/+4Iv", + "iZSombFru93e+StjiY/z/J1D8oj5kqSsrBgFKkVy8CUR6RxKrH8enh6955xx9TsDkXJSScJocqDeIFCv", + "EAdRMSoAlSyDYjsZJRVnFXBJQI9Rirzf/WIOtnsJQuAcVD9JZAHJQXIicvXXolJ/CMkJzZP7+1HC4bea", + "cMiSg1/0qFdtl4bQph+bfoZUJvej5LDOCDuzVPZJOQvoRzPGEVY9UA4UOFat+kzpFvpHUfwwSw5++ZL8", + "N4dZcpD8104rzR0ryp0TyAi+PDtO7q9GEUnYmSAzM2/3uDXT+fwGPEWYfsuyxSQHqhtesAu4k4rcAS5C", + "ki6rguHMUYNmpAAkGZoCkhxT1XIKmZLJjPESy+QgmRKK+SLp0NdX4igpQeIMS2xmneG6UP2/3CdduRxm", + "GVE/cYE+syki1ExGGLW0VFgIyNQfcg6oIhUUhIZ25OaK0aGUPSFZSEePio91nhOao+9x6gzk6DtUq4mV", + "oTh5VM5KmqlN0yw2NQdZczqRpAQhcVmJkAbJa+jRcab7oLaPmX4eqARJuJPb6LyuKsaVNd3gogZxgLYE", + "UAk0ha0R2rplPNsaIWXmyBCFpowVgCl6taUm31Lvtma4ELD1eht9ZyhDRCD7+lU73utt1xKVgKlAlHlE", + "btvZ7Dv1ezzFWmttG09qlsuLVjKrYKDnGDG7X+IeRyXO4YLpf/r+kdckwzSFiUhxAYGavt1+09XRe5qy", + "muMchLUU2WAIIFLqF2nBBBQLVBB63Rqv0huqOCsriV7NST4HbnWHSrxAHLI6tUOg32pcELl47cvtg6UT", + "nWs6G35pXU6BK36JY3DA083YkinKyWyBbomc9/xq2N2N/CK2rsedLJHjXl+O30HOQRNzOyepIaNFSEMp", + "EaiqxVyL8BbzTOhWhBJJcGHabHfpQ6vFVDCOxQpIOETH7OwQvTpmt+MzTK/RYYYrqZHptVU8phkiUqCU", + "cRMdM+Vlt0DyudSOa5jwAgx6f4fLqoAD9AX9mhRYApXjlFFBhHK0xU6RlmNF3Vhkd8WvyQHa294doV8T", + "Cpx8FjsVuYNijLkcu7f7974AjjVjz4aDPX7WhEIKOZbkBibG+FcQcdG6ySvxWrtXTTJAt3Ms1V9wlxZ1", + "BmjGWRkR8VFOGVcWNEOhQaJf693dr1K055P9yZKGTg1pMerrcmL8elIBj/Gw12XhkzY1xGYOEHyMqIBb", + "9gJC6hIdmcanwHvkECohN9ar6aEz4KBZk9AJLXu7u8P0ZEAZEUrHuuM2OmEczG9UixoXCrUAa8yyEGWh", + "yLEyrSUSBbsFjhoq1DBZXWjPnS5UvAGay3mPP9cenWuqY9z54l3HKpbZ5LBOBZ6BXEzSOaTXgfBU6OtK", + "7xS4wkQVSHU3pLtpUxSSlBr3Z13sUrBQF5lKYdhsBlQoI2MczTEvZ3Xhk3luRn2niWmItdFaUwuQ9SVy", + "DtYtOaYZK5HBtwFRqMZReTtdBVLY3f6fAbhmM5OKtGkarqqCtEGOg9Ox0cyrXfVmLwhk527OHjZ34n7l", + "FGgCWyQBCCL76gwgniCvHTYb1p8scj5hgtqoZF1Y/kNoPDzlkNd1dLtKpWvmdP8iGbC+SmcdUPwmtiCb", + "cVyC0IAsIGU00+Yd5CE3anifu+8HcGuuw34w55tvo7OalohQpMO5WGPSj2bw2Lxr224Tf7AZX8fPP9Vq", + "DRkPTydKplpPpnV6DbJLxd7+t10yLt2ESsV6tamIUiLHJaupVAowYzbLLT+h0DozoVC9sjCrfpYqdtqe", + "t6QoFNgTql/1VHhimr3VRAeM+aGdEQETXOeTAVje3e/lqQ0LujPCWdaCccCwSZfRx2DhYRcdHASU00Kn", + "zYN9TcJLUw5YOL6DEK8JOKxzNAzwq9OX/Tf/j7OXTV7hJHFLso717u3ufx3DQ93yQXD4ox67P+sDI4wJ", + "HUtCzDnkJVB5SBdyTmi+3w8zU3YX2TRFhTYg9DXCnOMFyskNUIQFwmjK7twWgPUzjYsjxf9PP//0MzJo", + "7HP7lt0Nrrn7kx85vBeG+MciPBbXE0KrWkb5Y7djDoIVtQY11Rjpxh2m5KIiqfZKvVjDqOJwQ1gt1I+M", + "pLo3kdauRm1Wpf1i7+7j3Y/o1cd//PiP/TffaJM8PzwJMskTNfORJvMvt+ot60J5sbiesFo2glyCB0cq", + "t65h1ErQRBVudwXnKgFXA5ptQVxOSV4rYRrRG7MSI8RmEqj6M6tTve8HUgK3PeUcU4U4hOYFeGoIuHKU", + "ox8M5THwoMqoCvI7TFLGeCYexl7FCJVI9yQUSxBNAG3GbZcUmOaAftkd7V1ZE9G97bwI7ipIpWk+BdOA", + "g1AP1SOjvoyUCisZFWHEsnOhd4aHGKP+ZH1n+HS3b72czSxXVhEdX7idAwcEOLXkI6IUh179NPr5dYt+", + "QSKtm3Up8/J3TViBp1BECDvWz5uMJiDNUbOHCM1IquWPVVPIOatpZlureL8bNJni9Npv0ifXTLtkQ7xg", + "OZEPsBbTTaCajpUHiDkrVIajzdOMhQgVUkV9NlMkaozT7yObzsdm9r6e140dvZiwJH5cVs1O6CMXnE+8", + "T/s0gFgbtrLH7weuSAG/ffMftIG1ljQ3O1mrMs4H7xw554z477t5Ta9jeU+qXugEVSlTeyVuD7n658fS", + "bjf1k149gM109ag+i+HWR6vrZqaBMd3r3sBEQqkIuvfmaMZqJtJhrCdJ6TdUhHmyNIKKSPDD6eU7Vla1", + "hCM6i5w9nzSH8BlITJT5fzi9RKnp4x8D94Vq4KvBunjuhT+bcoY2Ufzslw14/lpCyfhiMuMAQQf9GH2v", + "Hi/pJpnERaTfhX4e7UhohzT9ILodgMuApk/q75X7akog1LQMiAxZdTJyBHla7Sgvrt5LSQryu1bRKhUr", + "zdZtcyQklkRIkopHKveFNbaeGkaJx+PEWrLfzZMYsvKNTucPY2geGsVQvsZCeF2TiHEQJSi0lq4tRCzm", + "48XF6UCNkXq1ZpGRAYv1C3KaeqF+Qc53DnfMzAHidOVnp/WYbtkZ4PVfuCCZHq7heogVB85LOemO5yG5", + "4SQG4z613QFidGOe3WKuvd7KYq06KuXfSxE7r2qV75lKqqbg5zRos4z5DiB5nH2oajRkcn6yu9ZxiM2t", + "/fan7tkq1K3ahs28o5Zx33QiUl6ijHOJpVhLDRxwMVbRWitkGcgqsoQb95EK6fp8RymG7L+uVgz7EbUM", + "Ev4RcCHn71yeHUpUDVeLeFo21x2RaeJSM48yoHWpiP3hn8koeX929sNZMkqOvjt+75N3biZYxbClw+fL", + "IzvClV4qPqhqMrZMiaxuB4TRXVv42enq2krfykyxxKrs1dJy1em3rKbSO398kGB0Sr9MLsOrgVYqurZv", + "5VKgm6J30vIYBxFGj49P3s0ZSSEWnQpTvLlenD0+PjkxBb7J/dX9KJkRSsR8wgELF0O8zYbmKFW3Qmem", + "VWyLm2ZwF+SD+kE8uTPTP5LkrtXoeTyJtqKKy/GknT4UZMqoBCp9Jt7ZR7EtMlYEQHfGitUgx00jN1NI", + "tSMsTvYZ/FaDiFRElPhuItk10O7Z3Df+3vsdujBtlmhErJ3i+Brxq3vtMF1Ht5A+aF06qER3TCQHXAb9", + "dG1rWJ+Cy+gGhoSyUq5ac+gczX7rO23bKHICKlk1CfdqxnteZ1ahf0YlqvpV3cIzv9vpyoKaRimhlTg7", + "GLKSFgU71q194kEqtm50728imEH6Ck45KFAMnMc+ipZCrLN2dDaz0kxq59ArGNIucGnMNrbuKu0EaUN5", + "bT3SSa+ri6WI7U0XAZuyKkCv01rnbWHHvVzmtWbfLNLd7EAu66qXlJGeemE93DG6ceeGGUV46swUSs8T", + "T0x+5Ab0qa89/D3FHBur7aM2Z8Wk5sWKrfLLs2Md/EU91eX5hObohmB0wUl6rQ/YmGQpK+zGeabPUWyN", + "SEFubKHIWLJxtxoGVZo4Pxl4Z8hClzxqsXCjbPMBRFf1tCBirmg2fYdJd/mqS2MxzVDB8oC892aMAerW", + "PHv4hEvoJslIMsRr2pebemF+fGbTbfSJSZICkrpQeE4EIgJRXEKG3OSuDMgVXJs9XCbnwBFntQQx0ucD", + "RKKMgUCUSVPRqGbCKHowYiqE4A6n0jx7JV6jDCqgmUCMhpwQZc0lUGmLJ2mGSl0ONtUVNzOS1xxPC9Ca", + "UD3/bczg3wjzvHYH+mstnBrbbqT95b53ymZL6XVjkMC9A8PIpzbWXyKeZU2pNb5wr0VIQg3Hyvaselkt", + "c2ZOclS8VSK2wwRzmkdDZuUcD+JTn7Oap+DPSmjKynDWZgwkgzqT8+Z5dPLu2iugJBSJD1JxDFoDrR60", + "GlkOLv3FycMhT6OZq6RbPt0zYpgPvJI5HFsGWWsj1tPaNJLsz7PqlzbqZUnMCRbX4kG2bPq6iqYBA/br", + "Crrra45vR6imXmlJW/gi0CvT9XUDfbpSJvyuJqwaCOukVm519MbTIojqPWV8aOtEy2NLmDCR6VNo01zT", + "rQtLwikDKDMDr/ze1hImXHMr1asO7Uv1q3dqIqenpXrhlKnwBhNTXOp99ImnrJad+j/dr69wKma3/Wl+", + "nIN0pbpmwlss0KzAeQ4ZwgJ9Ov/+x+BcXw2z/lm10oR6Y8oh/LrqZsa16iOjfq0GV05tqnNaFlJMVYKA", + "0xSEMB/lNudBazixcV1hSNFi8/Wp1TWkx8uz45gqNfpyVtpv9wapDDX20jx3uVTMRBh9+p0+ffAu1tnr", + "M2f062+DmhP3+84RfGzx/Ly7jSPH41XYexkwqPf2U52hhdff51Pcp/wepPeh65LvQTbftm6+bf37ftv6", + "5j/601Z0DmqhLgHp2vLKbNLoWmO9j7H1v1vKNERzM8R00VYgb8oJ/7QPWHr4veYHLNZgOiE2DKGDcfa8", + "AkjnQ4E24MKHrENUKjwRFeBr4CgDtbLnQum4UOBfLBDcVRyE1psKE5hqVWeqD6RzVxapjE7bqnqc6ZYV", + "kan2nN5S2v2lZOemVktYCWDTLfWXGT+uR2+QZ/zOdh1KlkWLNilbHiLMFx16f2PZVIP5WmgvgSlEDGZl", + "sVLB0uCMB9OFPWLtcvilZ9NX934MTzsFL+1hnrk/qnPsF5WhftA21TSjC/V0Veqq+DBT2Zaea61RIPXw", + "XbjV+27mG+RVibr7Yle1DdYKD6yd6K4R3Em8IWJFLYUl1ZfZ8r0ejdBpzYlcnCtSDJ8fLy5O3wLmwJtr", + "yDSsm0fNIHMpq+T+XpcBxOpLD+3VA2lzWxSvKTo8avb9/I2+Y3IDlcKSwyN0VlOqJ1K4Zsba3d7d3lUC", + "YRVQXJHkIPlqe297V2kLy7kme0dfQjSWbOycuGIiFs2bm5q8i7XMx0B2tcUqaw1HmVpKdG8x4uYw9i3L", + "Fp0KAhP1MZc7KuyO3QVbRs2rjCB2ZdJ9qGIV4/UDo1DN9v7ubocKT+o7n219x3okBAtEPXcncNd6sT+r", + "C9Q2GyVfPyEJbeFmZP63OEPuKFzPu/cy815SXMs54+R3yPTEe1+9zMSWWfSeSpUGXzCGjjE3BRhf7715", + "Ke7bhFUjlcFyRcL+/pOS0Cui7RPTNkFNoe2bl7K/IyqBU1ygc+A3wB0FHozqmOsD6C9X91ejRNRlifnC", + "3ciHLhhyqQHOhcJuF0oUet+NTYqFxWJMcQljdgOck0wjf4AOo2RnbusidxwK56BFEIKYX9SaPCOCxIpn", + "1wWSe19ObiBTPRxy2pTGLmXVFYo+O69moj/GpRtDsakLQofZM6+fky+vIvVxXBkSNTd6aaWCcvNJZTwq", + "H1ZVsXDfVQZX1whztF9xppIsb7HWC9Odu4aeOU4Hs71woA5rZDeRejhSbyLUQyOUuaDigqHmK+UHhigS", + "OoYPAmtk5nrDyuDA6sQ8vIrqZRz+z0jMYwXjG6//i+fnG+h5NPQ8MjkmgYf6wHPT3EIXRZ4PsbvXHpR0", + "uLuKXgaDzGwvDELhZtIGfjZJxzN4fnPn1+Nc3znGKNkpyA2Mw4rHVcuP6MLDq2Y2tXv+Xaqy5hQyBDTT", + "1+2IKER0i++WwsTjdTRQuPrCKDFYabgBjA1gPB1gKDMzYPFHUKPoeqZBjqJcI1XQZ421rmfAqMA0rxWE", + "NUf5fRQ4Pnkux28/EHtpZ/c+h9r498a/n9C/tbc82J+L0riwLUUfY3sN3Hh/2KPtjXG28Fl//YXpkow/", + "csPcM2f9vRlf2M3DkvKNo28c/ekc3XmfM260/wi/F30HGSU7KkKvcfTwoVORrNf+XgFyPKn3Kr2eKaz3", + "a8k2pwwbt/+buL2uovsDhwzSc7/A2U093lpbfWEX/7/ZM/87mvs22G0CyrbyD9PMK8EM/u+5AaQwNX7P", + "ChVBGeELY0X4PyFusGKDFU+PFY0LPQ4sbHeNFrV383MUJuzts81KAE0X7r/W0J9ESoHaC/ajbt/eX/vM", + "qwM30SY72Hj838TjvbufH+jqte8MQhMg9HSdy/ddvfG7gtUZesfKsqZELtAHLOEWLxL7AbCuchYHOzsZ", + "B1yOc/N2u7Ddt1PVXZfVD4x/LnVWMTRsM5DQ7XZwRXamIPFOw+/91f3/BQAA///XTK/wGnoAAA==", +} + +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { + zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) + if err != nil { + return nil, fmt.Errorf("error base64 decoding spec: %w", err) + } + zr, err := gzip.NewReader(bytes.NewReader(zipped)) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %w", err) + } + var buf bytes.Buffer + _, err = buf.ReadFrom(zr) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %w", err) + } + + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + res := make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.T, err error) { + resolvePath := PathToRawSpec("") + + loader := openapi3.NewLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) { + pathToFile := url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadFromData(specData) + if err != nil { + return + } + return +} diff --git a/ai/worker/worker.go b/ai/worker/worker.go new file mode 100644 index 0000000000..2cc1608fba --- /dev/null +++ b/ai/worker/worker.go @@ -0,0 +1,819 @@ +package worker + +import ( + "bufio" + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "log/slog" + "net/http" + "strconv" + "strings" + "sync" + + docker "github.com/docker/docker/client" +) + +// EnvValue unmarshals JSON booleans as strings for compatibility with env variables. +type EnvValue string + +// UnmarshalJSON converts JSON booleans to strings for EnvValue. +func (sb *EnvValue) UnmarshalJSON(b []byte) error { + var boolVal bool + err := json.Unmarshal(b, &boolVal) + if err == nil { + *sb = EnvValue(strconv.FormatBool(boolVal)) + return nil + } + + var strVal string + err = json.Unmarshal(b, &strVal) + if err == nil { + *sb = EnvValue(strVal) + } + + return err +} + +// String returns the string representation of the EnvValue. +func (sb EnvValue) String() string { + return string(sb) +} + +// OptimizationFlags is a map of optimization flags to be passed to the pipeline. +type OptimizationFlags map[string]EnvValue + +type Worker struct { + manager *DockerManager + externalContainers map[string]*RunnerContainer + mu *sync.Mutex +} + +func NewWorker(imageOverrides ImageOverrides, verboseLogs bool, gpus []string, modelDir string) (*Worker, error) { + dockerClient, err := docker.NewClientWithOpts(docker.FromEnv, docker.WithAPIVersionNegotiation()) + if err != nil { + return nil, err + } + + manager, err := NewDockerManager(imageOverrides, verboseLogs, gpus, modelDir, dockerClient) + if err != nil { + return nil, err + } + + return &Worker{ + manager: manager, + externalContainers: make(map[string]*RunnerContainer), + mu: &sync.Mutex{}, + }, nil +} + +func (w *Worker) HardwareInformation() []HardwareInformation { + var hardware []HardwareInformation + for _, rc := range w.externalContainers { + if rc.Hardware != nil { + hardware = append(hardware, *rc.Hardware) + } else { + hardware = append(hardware, HardwareInformation{}) + } + } + + for _, rc := range w.manager.containers { + if rc.Hardware != nil { + hardware = append(hardware, *rc.Hardware) + } else { + hardware = append(hardware, HardwareInformation{}) + } + } + + return hardware +} + +func (w *Worker) TextToImage(ctx context.Context, req GenTextToImageJSONRequestBody) (*ImageResponse, error) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + c, err := w.borrowContainer(ctx, "text-to-image", *req.ModelId) + if err != nil { + return nil, err + } + + resp, err := c.Client.GenTextToImageWithResponse(ctx, req) + if err != nil { + return nil, err + } + + if resp.JSON400 != nil { + val, err := json.Marshal(resp.JSON400) + if err != nil { + return nil, err + } + slog.Error("text-to-image container returned 400", slog.String("err", string(val))) + return nil, errors.New("text-to-image container returned 400: " + resp.JSON400.Detail.Msg) + } + + if resp.JSON401 != nil { + val, err := json.Marshal(resp.JSON401) + if err != nil { + return nil, err + } + slog.Error("text-to-image container returned 401", slog.String("err", string(val))) + return nil, errors.New("text-to-image container returned 401: " + resp.JSON401.Detail.Msg) + } + + if resp.JSON422 != nil { + val, err := json.Marshal(resp.JSON422) + if err != nil { + return nil, err + } + slog.Error("text-to-image container returned 422", slog.String("err", string(val))) + return nil, errors.New("text-to-image container returned 422: " + string(val)) + } + + if resp.JSON500 != nil { + val, err := json.Marshal(resp.JSON500) + if err != nil { + return nil, err + } + slog.Error("text-to-image container returned 500", slog.String("err", string(val))) + return nil, errors.New("text-to-image container returned 500: " + resp.JSON500.Detail.Msg) + } + + return resp.JSON200, nil +} + +func (w *Worker) ImageToImage(ctx context.Context, req GenImageToImageMultipartRequestBody) (*ImageResponse, error) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + c, err := w.borrowContainer(ctx, "image-to-image", *req.ModelId) + if err != nil { + return nil, err + } + + var buf bytes.Buffer + mw, err := NewImageToImageMultipartWriter(&buf, req) + if err != nil { + return nil, err + } + + resp, err := c.Client.GenImageToImageWithBodyWithResponse(ctx, mw.FormDataContentType(), &buf) + if err != nil { + return nil, err + } + + if resp.JSON400 != nil { + val, err := json.Marshal(resp.JSON400) + if err != nil { + return nil, err + } + slog.Error("image-to-image container returned 400", slog.String("err", string(val))) + return nil, errors.New("image-to-image container returned 400: " + resp.JSON400.Detail.Msg) + } + + if resp.JSON401 != nil { + val, err := json.Marshal(resp.JSON401) + if err != nil { + return nil, err + } + slog.Error("image-to-image container returned 401", slog.String("err", string(val))) + return nil, errors.New("image-to-image container returned 401: " + resp.JSON401.Detail.Msg) + } + + if resp.JSON422 != nil { + val, err := json.Marshal(resp.JSON422) + if err != nil { + return nil, err + } + slog.Error("image-to-image container returned 422", slog.String("err", string(val))) + return nil, errors.New("image-to-image container returned 422: " + string(val)) + } + + if resp.JSON500 != nil { + val, err := json.Marshal(resp.JSON500) + if err != nil { + return nil, err + } + slog.Error("image-to-image container returned 500", slog.String("err", string(val))) + return nil, errors.New("image-to-image container returned 500: " + resp.JSON500.Detail.Msg) + } + + return resp.JSON200, nil +} + +func (w *Worker) ImageToVideo(ctx context.Context, req GenImageToVideoMultipartRequestBody) (*VideoResponse, error) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + c, err := w.borrowContainer(ctx, "image-to-video", *req.ModelId) + if err != nil { + return nil, err + } + + var buf bytes.Buffer + mw, err := NewImageToVideoMultipartWriter(&buf, req) + if err != nil { + return nil, err + } + + resp, err := c.Client.GenImageToVideoWithBodyWithResponse(ctx, mw.FormDataContentType(), &buf) + if err != nil { + return nil, err + } + + if resp.JSON400 != nil { + val, err := json.Marshal(resp.JSON400) + if err != nil { + return nil, err + } + slog.Error("image-to-video container returned 400", slog.String("err", string(val))) + return nil, errors.New("image-to-video container returned 400: " + resp.JSON400.Detail.Msg) + } + + if resp.JSON401 != nil { + val, err := json.Marshal(resp.JSON401) + if err != nil { + return nil, err + } + slog.Error("image-to-video container returned 401", slog.String("err", string(val))) + return nil, errors.New("image-to-video container returned 401: " + resp.JSON401.Detail.Msg) + } + + if resp.JSON422 != nil { + val, err := json.Marshal(resp.JSON422) + if err != nil { + return nil, err + } + slog.Error("image-to-video container returned 422", slog.String("err", string(val))) + return nil, errors.New("image-to-video container returned 422: " + string(val)) + } + + if resp.JSON500 != nil { + val, err := json.Marshal(resp.JSON500) + if err != nil { + return nil, err + } + slog.Error("image-to-video container returned 500", slog.String("err", string(val))) + return nil, errors.New("image-to-video container returned 500: " + resp.JSON500.Detail.Msg) + } + + if resp.JSON200 == nil { + slog.Error("image-to-video container returned no content") + return nil, errors.New("image-to-video container returned no content") + } + + return resp.JSON200, nil +} + +func (w *Worker) Upscale(ctx context.Context, req GenUpscaleMultipartRequestBody) (*ImageResponse, error) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + c, err := w.borrowContainer(ctx, "upscale", *req.ModelId) + if err != nil { + return nil, err + } + + var buf bytes.Buffer + mw, err := NewUpscaleMultipartWriter(&buf, req) + if err != nil { + return nil, err + } + + resp, err := c.Client.GenUpscaleWithBodyWithResponse(ctx, mw.FormDataContentType(), &buf) + if err != nil { + return nil, err + } + + if resp.JSON400 != nil { + val, err := json.Marshal(resp.JSON400) + if err != nil { + return nil, err + } + slog.Error("upscale container returned 400", slog.String("err", string(val))) + return nil, errors.New("upscale container returned 400: " + resp.JSON400.Detail.Msg) + } + + if resp.JSON401 != nil { + val, err := json.Marshal(resp.JSON401) + if err != nil { + return nil, err + } + slog.Error("upscale container returned 401", slog.String("err", string(val))) + return nil, errors.New("upscale container returned 401: " + resp.JSON401.Detail.Msg) + } + + if resp.JSON422 != nil { + val, err := json.Marshal(resp.JSON422) + if err != nil { + return nil, err + } + slog.Error("upscale container returned 422", slog.String("err", string(val))) + return nil, errors.New("upscale container returned 422: " + string(val)) + } + + if resp.JSON500 != nil { + val, err := json.Marshal(resp.JSON500) + if err != nil { + return nil, err + } + slog.Error("upscale container returned 500", slog.String("err", string(val))) + return nil, errors.New("upscale container returned 500: " + resp.JSON500.Detail.Msg) + } + + return resp.JSON200, nil +} + +func (w *Worker) AudioToText(ctx context.Context, req GenAudioToTextMultipartRequestBody) (*TextResponse, error) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + c, err := w.borrowContainer(ctx, "audio-to-text", *req.ModelId) + if err != nil { + return nil, err + } + + var buf bytes.Buffer + mw, err := NewAudioToTextMultipartWriter(&buf, req) + if err != nil { + return nil, err + } + + resp, err := c.Client.GenAudioToTextWithBodyWithResponse(ctx, mw.FormDataContentType(), &buf) + if err != nil { + return nil, err + } + + if resp.JSON400 != nil { + val, err := json.Marshal(resp.JSON400) + if err != nil { + return nil, err + } + slog.Error("audio-to-text container returned 400", slog.String("err", string(val))) + return nil, errors.New("audio-to-text container returned 400: " + resp.JSON400.Detail.Msg) + } + + if resp.JSON401 != nil { + val, err := json.Marshal(resp.JSON401) + if err != nil { + return nil, err + } + slog.Error("audio-to-text container returned 401", slog.String("err", string(val))) + return nil, errors.New("audio-to-text container returned 401: " + resp.JSON401.Detail.Msg) + } + + if resp.JSON413 != nil { + msg := "audio-to-text container returned 413: file too large; max file size is 50MB" + slog.Error("audio-to-text container returned 413", slog.String("err", string(msg))) + return nil, errors.New(msg) + } + + if resp.JSON415 != nil { + val, err := json.Marshal(resp.JSON415) + if err != nil { + return nil, err + } + slog.Error("audio-to-text container returned 415", slog.String("err", string(val))) + return nil, errors.New("audio-to-text container returned 415: " + resp.JSON415.Detail.Msg) + } + + if resp.JSON422 != nil { + val, err := json.Marshal(resp.JSON422) + if err != nil { + return nil, err + } + slog.Error("audio-to-text container returned 422", slog.String("err", string(val))) + return nil, errors.New("audio-to-text container returned 422: " + string(val)) + } + + if resp.JSON500 != nil { + val, err := json.Marshal(resp.JSON500) + if err != nil { + return nil, err + } + slog.Error("audio-to-text container returned 500", slog.String("err", string(val))) + return nil, errors.New("audio-to-text container returned 500: " + resp.JSON500.Detail.Msg) + } + + return resp.JSON200, nil +} + +func (w *Worker) LLM(ctx context.Context, req GenLLMJSONRequestBody) (interface{}, error) { + isStreaming := req.Stream != nil && *req.Stream + ctx, cancel := context.WithCancel(ctx) + c, err := w.borrowContainer(ctx, "llm", *req.Model) + if err != nil { + cancel() + return nil, err + } + if c == nil { + cancel() + return nil, errors.New("borrowed container is nil") + } + if c.Client == nil { + cancel() + return nil, errors.New("container client is nil") + } + + slog.Info("Container borrowed successfully", "model_id", *req.Model) + + if isStreaming { + resp, err := c.Client.GenLLM(ctx, req) + if err != nil { + cancel() + return nil, err + } + return w.handleStreamingResponse(ctx, c, resp, cancel) + } + + defer cancel() + resp, err := c.Client.GenLLMWithResponse(ctx, req) + if err != nil { + return nil, err + } + return w.handleNonStreamingResponse(c, resp) +} + +func (w *Worker) SegmentAnything2(ctx context.Context, req GenSegmentAnything2MultipartRequestBody) (*MasksResponse, error) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + c, err := w.borrowContainer(ctx, "segment-anything-2", *req.ModelId) + if err != nil { + return nil, err + } + + var buf bytes.Buffer + mw, err := NewSegmentAnything2MultipartWriter(&buf, req) + if err != nil { + return nil, err + } + + resp, err := c.Client.GenSegmentAnything2WithBodyWithResponse(ctx, mw.FormDataContentType(), &buf) + if err != nil { + return nil, err + } + + if resp.JSON400 != nil { + val, err := json.Marshal(resp.JSON400) + if err != nil { + return nil, err + } + slog.Error("segment anything 2 container returned 400", slog.String("err", string(val))) + return nil, errors.New("segment anything 2 container returned 400: " + resp.JSON400.Detail.Msg) + } + + if resp.JSON401 != nil { + val, err := json.Marshal(resp.JSON401) + if err != nil { + return nil, err + } + slog.Error("segment anything 2 container returned 401", slog.String("err", string(val))) + return nil, errors.New("segment anything 2 container returned 401: " + resp.JSON401.Detail.Msg) + } + + if resp.JSON422 != nil { + val, err := json.Marshal(resp.JSON422) + if err != nil { + return nil, err + } + slog.Error("segment anything 2 container returned 422", slog.String("err", string(val))) + return nil, errors.New("segment anything 2 container returned 422: " + string(val)) + } + + if resp.JSON500 != nil { + val, err := json.Marshal(resp.JSON500) + if err != nil { + return nil, err + } + slog.Error("segment anything 2 container returned 500", slog.String("err", string(val))) + return nil, errors.New("segment anything 2 container returned 500: " + resp.JSON500.Detail.Msg) + } + + return resp.JSON200, nil +} + +func (w *Worker) ImageToText(ctx context.Context, req GenImageToTextMultipartRequestBody) (*ImageToTextResponse, error) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + c, err := w.borrowContainer(ctx, "image-to-text", *req.ModelId) + if err != nil { + return nil, err + } + + var buf bytes.Buffer + mw, err := NewImageToTextMultipartWriter(&buf, req) + if err != nil { + return nil, err + } + + resp, err := c.Client.GenImageToTextWithBodyWithResponse(ctx, mw.FormDataContentType(), &buf) + if err != nil { + return nil, err + } + + if resp.JSON400 != nil { + val, err := json.Marshal(resp.JSON400) + if err != nil { + return nil, err + } + slog.Error("image-to-text container returned 400", slog.String("err", string(val))) + return nil, errors.New("image-to-text container returned 400: " + resp.JSON400.Detail.Msg) + } + + if resp.JSON401 != nil { + val, err := json.Marshal(resp.JSON401) + if err != nil { + return nil, err + } + slog.Error("image-to-text container returned 401", slog.String("err", string(val))) + return nil, errors.New("image-to-text container returned 401: " + resp.JSON401.Detail.Msg) + } + + if resp.JSON413 != nil { + msg := "image-to-text container returned 413 file too large; max file size is 50MB" + slog.Error("image-to-text container returned 413", slog.String("err", string(msg))) + return nil, errors.New(msg) + } + + if resp.JSON422 != nil { + val, err := json.Marshal(resp.JSON422) + if err != nil { + return nil, err + } + slog.Error("image-to-text container returned 422", slog.String("err", string(val))) + return nil, errors.New("image-to-text container returned 422: " + string(val)) + } + + if resp.JSON500 != nil { + val, err := json.Marshal(resp.JSON500) + if err != nil { + return nil, err + } + slog.Error("image-to-text container returned 500", slog.String("err", string(val))) + return nil, errors.New("image-to-text container returned 500: " + resp.JSON500.Detail.Msg) + } + + return resp.JSON200, nil +} + +func (w *Worker) TextToSpeech(ctx context.Context, req GenTextToSpeechJSONRequestBody) (*AudioResponse, error) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + c, err := w.borrowContainer(ctx, "text-to-speech", *req.ModelId) + if err != nil { + return nil, err + } + + resp, err := c.Client.GenTextToSpeechWithResponse(ctx, req) + if err != nil { + return nil, err + } + + if resp.JSON400 != nil { + val, err := json.Marshal(resp.JSON400) + if err != nil { + return nil, err + } + slog.Error("text-to-speech container returned 400", slog.String("err", string(val))) + return nil, errors.New("text-to-speech container returned 400: " + resp.JSON400.Detail.Msg) + } + + if resp.JSON401 != nil { + val, err := json.Marshal(resp.JSON401) + if err != nil { + return nil, err + } + slog.Error("text-to-speech container returned 401", slog.String("err", string(val))) + return nil, errors.New("text-to-speech container returned 401: " + resp.JSON401.Detail.Msg) + } + + if resp.JSON422 != nil { + val, err := json.Marshal(resp.JSON422) + if err != nil { + return nil, err + } + slog.Error("text-to-speech container returned 422", slog.String("err", string(val))) + return nil, errors.New("text-to-speech container returned 422: " + string(val)) + } + + if resp.JSON500 != nil { + val, err := json.Marshal(resp.JSON500) + if err != nil { + return nil, err + } + slog.Error("text-to-speech container returned 500", slog.String("err", string(val))) + return nil, errors.New("text-to-speech container returned 500: " + resp.JSON500.Detail.Msg) + } + + return resp.JSON200, nil +} + +func (w *Worker) LiveVideoToVideo(ctx context.Context, requestID, streamID string, req GenLiveVideoToVideoJSONRequestBody) (*LiveVideoToVideoResponse, error) { + // Live video containers keep running after the initial request, so we use a background context to borrow the container. + c, err := w.borrowContainer(context.Background(), "live-video-to-video", *req.ModelId) + if err != nil { + return nil, err + } + + setHeaders := func(ctx context.Context, req *http.Request) error { + req.Header.Set("requestID", requestID) + req.Header.Set("streamID", streamID) + return nil + } + resp, err := c.Client.GenLiveVideoToVideoWithResponse(ctx, req, setHeaders) + if err != nil { + return nil, err + } + + if resp.JSON400 != nil { + val, err := json.Marshal(resp.JSON400) + if err != nil { + return nil, err + } + slog.Error("live-video-to-video container returned 400", slog.String("err", string(val))) + return nil, errors.New("live-video-to-video container returned 400: " + resp.JSON400.Detail.Msg) + } + + if resp.JSON401 != nil { + val, err := json.Marshal(resp.JSON401) + if err != nil { + return nil, err + } + slog.Error("live-video-to-video container returned 401", slog.String("err", string(val))) + return nil, errors.New("live-video-to-video container returned 401: " + resp.JSON401.Detail.Msg) + } + + if resp.JSON422 != nil { + val, err := json.Marshal(resp.JSON422) + if err != nil { + return nil, err + } + slog.Error("live-video-to-video container returned 422", slog.String("err", string(val))) + return nil, errors.New("live-video-to-video container returned 422: " + string(val)) + } + + if resp.JSON500 != nil { + val, err := json.Marshal(resp.JSON500) + if err != nil { + return nil, err + } + slog.Error("live-video-to-video container returned 500", slog.String("err", string(val))) + return nil, errors.New("live-video-to-video container returned 500: " + resp.JSON500.Detail.Msg) + } + + return resp.JSON200, nil +} + +func (w *Worker) EnsureImageAvailable(ctx context.Context, pipeline string, modelID string) error { + return w.manager.EnsureImageAvailable(ctx, pipeline, modelID) +} + +func (w *Worker) Warm(ctx context.Context, pipeline string, modelID string, endpoint RunnerEndpoint, optimizationFlags OptimizationFlags) error { + if endpoint.URL == "" { + return w.manager.Warm(ctx, pipeline, modelID, optimizationFlags) + } + + w.mu.Lock() + defer w.mu.Unlock() + + cfg := RunnerContainerConfig{ + Type: External, + Pipeline: pipeline, + ModelID: modelID, + Endpoint: endpoint, + containerTimeout: externalContainerTimeout, + } + rc, err := NewRunnerContainer(ctx, cfg, endpoint.URL) + if err != nil { + return err + } + + name := dockerContainerName(pipeline, modelID, endpoint.URL) + slog.Info("Starting external container", slog.String("name", name), slog.String("modelID", modelID)) + w.externalContainers[name] = rc + + return nil +} + +func (w *Worker) Stop(ctx context.Context) error { + if err := w.manager.Stop(ctx); err != nil { + return err + } + + w.mu.Lock() + defer w.mu.Unlock() + + for name := range w.externalContainers { + delete(w.externalContainers, name) + } + + return nil +} + +// HasCapacity returns true if the worker has capacity for the given pipeline and model ID. +func (w *Worker) HasCapacity(pipeline, modelID string) bool { + w.mu.Lock() + defer w.mu.Unlock() + + // Check if we have capacity for external containers. + for _, rc := range w.externalContainers { + if rc.Pipeline == pipeline && rc.ModelID == modelID { + return true + } + } + + // Check if we have capacity for managed containers. + return w.manager.HasCapacity(context.Background(), pipeline, modelID) +} + +func (w *Worker) borrowContainer(ctx context.Context, pipeline, modelID string) (*RunnerContainer, error) { + w.mu.Lock() + + for _, rc := range w.externalContainers { + if rc.Pipeline == pipeline && rc.ModelID == modelID { + w.mu.Unlock() + // Assume external containers can handle concurrent in-flight requests. + return rc, nil + } + } + + w.mu.Unlock() + + return w.manager.Borrow(ctx, pipeline, modelID) +} + +func (w *Worker) handleNonStreamingResponse(c *RunnerContainer, resp *GenLLMResponse) (*LLMResponse, error) { + if resp.JSON400 != nil { + val, err := json.Marshal(resp.JSON400) + if err != nil { + return nil, err + } + slog.Error("LLM container returned 400", slog.String("err", string(val))) + return nil, errors.New("LLM container returned 400: " + resp.JSON400.Detail.Msg) + } + + if resp.JSON401 != nil { + val, err := json.Marshal(resp.JSON401) + if err != nil { + return nil, err + } + slog.Error("LLM container returned 401", slog.String("err", string(val))) + return nil, errors.New("LLM container returned 401: " + resp.JSON401.Detail.Msg) + } + + if resp.JSON500 != nil { + val, err := json.Marshal(resp.JSON500) + if err != nil { + return nil, err + } + slog.Error("LLM container returned 500", slog.String("err", string(val))) + return nil, errors.New("LLM container returned 500: " + resp.JSON500.Detail.Msg) + } + + return resp.JSON200, nil +} + +func (w *Worker) handleStreamingResponse(ctx context.Context, c *RunnerContainer, resp *http.Response, returnContainer func()) (<-chan *LLMResponse, error) { + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) + } + + outputChan := make(chan *LLMResponse, 10) + + go func() { + defer close(outputChan) + defer returnContainer() + + scanner := bufio.NewScanner(resp.Body) + + for scanner.Scan() { + select { + case <-ctx.Done(): + return + default: + line := scanner.Text() + data := strings.TrimPrefix(line, "data: ") + if data == "" { + continue + } + if data == "[DONE]" { + break + } + + var llmRes LLMResponse + if err := json.Unmarshal([]byte(data), &llmRes); err != nil { + slog.Error("Error unmarshaling stream data", slog.String("err", err.Error()), slog.String("json", data)) + continue + } + + select { + case outputChan <- &llmRes: + case <-ctx.Done(): + return + } + } + } + + if err := scanner.Err(); err != nil { + slog.Error("Error reading stream", slog.String("err", err.Error())) + } + }() + + return outputChan, nil +} diff --git a/clog/clog.go b/clog/clog.go index 2402e3a714..c30ad3bc60 100644 --- a/clog/clog.go +++ b/clog/clog.go @@ -52,13 +52,15 @@ func init() { } type values struct { - mu sync.RWMutex - vals map[string]string + mu sync.RWMutex + keysOrder []string + vals map[string]string } func newValues() *values { return &values{ - vals: make(map[string]string), + vals: make(map[string]string), + keysOrder: []string{}, } } @@ -71,6 +73,7 @@ func Clone(parentCtx, logCtx context.Context) context.Context { cmap.mu.RLock() for k, v := range cmap.vals { newCmap.vals[k] = v + newCmap.keysOrder = append(newCmap.keysOrder, k) } cmap.mu.RUnlock() } @@ -109,6 +112,10 @@ func AddVal(ctx context.Context, key, val string) context.Context { ctx = context.WithValue(ctx, clogContextKey, cmap) } cmap.mu.Lock() + // add to keysOrder only if the key is not already present to avoid duplicate fields + if _, ok := cmap.vals[key]; !ok { + cmap.keysOrder = append(cmap.keysOrder, key) + } cmap.vals[key] = val cmap.mu.Unlock() return ctx @@ -186,6 +193,48 @@ func infof(ctx context.Context, lastErr bool, publicLog bool, format string, arg } } +// Info logs a message with key-value pairs in a slog-like style. +// Example: Info(ctx, "hello", "key1", value1, "key2", value2) +// This will log: "hello key1=value1 key2=value2" +func Info(ctx context.Context, msg string, keyvals ...interface{}) { + if len(keyvals)%2 != 0 { + keyvals = append(keyvals[:len(keyvals)-1], "MISSING", keyvals[len(keyvals)-1]) + } + + var sb strings.Builder + sb.WriteString(msg) + + for i := 0; i < len(keyvals); i += 2 { + key, ok := keyvals[i].(string) + if !ok { + key = fmt.Sprintf("%v", keyvals[i]) + } + + sb.WriteString(" ") + sb.WriteString(key) + sb.WriteString("=") + + val := keyvals[i+1] + switch v := val.(type) { + case string: + sb.WriteString(v) + case fmt.Stringer: + sb.WriteString(v.String()) + default: + sb.WriteString(fmt.Sprintf("%v", v)) + } + } + + Infof(ctx, "%s", sb.String()) +} + +// V returns a Verbose instance for conditional logging at the specified level +func (v Verbose) Info(ctx context.Context, msg string, keyvals ...interface{}) { + if v { + Info(ctx, msg, keyvals...) + } +} + func messageFromContext(ctx context.Context, sb *strings.Builder) { if ctx == nil { return @@ -203,11 +252,11 @@ func messageFromContext(ctx context.Context, sb *strings.Builder) { sb.WriteString(" ") } } - for key, val := range cmap.vals { + for _, key := range cmap.keysOrder { if _, ok := stdKeys[key]; !ok { sb.WriteString(key) sb.WriteString("=") - sb.WriteString(val) + sb.WriteString(cmap.vals[key]) sb.WriteString(" ") } } diff --git a/clog/clog_test.go b/clog/clog_test.go index d16997bee7..3613f2f68e 100644 --- a/clog/clog_test.go +++ b/clog/clog_test.go @@ -22,8 +22,9 @@ func TestStdKeys(t *testing.T) { assert.Equal("manifestID=manID sessionID=sessionID nonce=1038 seqNo=9427 orchSessionID=orchID ethaddress=0x0 orchestrator=http://127.0.0.1:8935 customKey=customVal testing message num=452", msg) ctxCloned := Clone(context.Background(), ctx) ctxCloned = AddManifestID(ctxCloned, "newManifest") + ctxCloned = AddVal(ctxCloned, "customKey2", "customVal2") msgCloned, _ := formatMessage(ctxCloned, false, false, "testing message num=%d", 4521) - assert.Equal("manifestID=newManifest sessionID=sessionID nonce=1038 seqNo=9427 orchSessionID=orchID ethaddress=0x0 orchestrator=http://127.0.0.1:8935 customKey=customVal testing message num=4521", msgCloned) + assert.Equal("manifestID=newManifest sessionID=sessionID nonce=1038 seqNo=9427 orchSessionID=orchID ethaddress=0x0 orchestrator=http://127.0.0.1:8935 customKey=customVal customKey2=customVal2 testing message num=4521", msgCloned) // old context shouldn't change msg, _ = formatMessage(ctx, false, false, "testing message num=%d", 452) assert.Equal("manifestID=manID sessionID=sessionID nonce=1038 seqNo=9427 orchSessionID=orchID ethaddress=0x0 orchestrator=http://127.0.0.1:8935 customKey=customVal testing message num=452", msg) diff --git a/cmd/livepeer/livepeer.go b/cmd/livepeer/livepeer.go index b4564ea170..e7734075f1 100755 --- a/cmd/livepeer/livepeer.go +++ b/cmd/livepeer/livepeer.go @@ -162,7 +162,9 @@ func parseLivepeerConfig() starter.LivepeerConfig { 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, "[Deprecated] Specify the base Docker image for the AI runner. Example: livepeer/ai-runner:0.0.1. Use -aiRunnerImageOverrides instead.") + cfg.AIVerboseLogs = flag.Bool("aiVerboseLogs", *cfg.AIVerboseLogs, "Set to true to enable verbose logs for the AI runner containers created by the worker") cfg.AIRunnerImageOverrides = flag.String("aiRunnerImageOverrides", *cfg.AIRunnerImageOverrides, `Specify overrides for the Docker images used by the AI runner. Example: '{"default": "livepeer/ai-runner:v1.0", "batch": {"text-to-speech": "livepeer/ai-runner:text-to-speech-v1.0"}, "live": {"another-pipeline": "livepeer/ai-runner:another-pipeline-v1.0"}}'`) + cfg.AIProcessingRetryTimeout = flag.Duration("aiProcessingRetryTimeout", *cfg.AIProcessingRetryTimeout, "Timeout for retrying to initiate AI processing request") // Live AI: cfg.MediaMTXApiPassword = flag.String("mediaMTXApiPassword", "", "HTTP basic auth password for MediaMTX API requests") diff --git a/cmd/livepeer/starter/kafka.go b/cmd/livepeer/starter/kafka.go index 880b04f1f1..e6f9d8e27e 100644 --- a/cmd/livepeer/starter/kafka.go +++ b/cmd/livepeer/starter/kafka.go @@ -11,9 +11,9 @@ func startKafkaProducer(cfg LivepeerConfig) error { return nil } - var broadcasterEthAddress = "" - if cfg.EthAcctAddr != nil { - broadcasterEthAddress = *cfg.EthAcctAddr + var gatewayHost = "" + if cfg.GatewayHost != nil { + gatewayHost = *cfg.GatewayHost } return lpmon.InitKafkaProducer( @@ -21,6 +21,6 @@ func startKafkaProducer(cfg LivepeerConfig) error { *cfg.KafkaUsername, *cfg.KafkaPassword, *cfg.KafkaGatewayTopic, - broadcasterEthAddress, + gatewayHost, ) } diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index 45285952b5..7766b93a76 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -27,7 +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/ai/worker" "github.com/livepeer/go-livepeer/build" "github.com/livepeer/go-livepeer/common" "github.com/livepeer/go-livepeer/core" @@ -165,6 +165,8 @@ type LivepeerConfig struct { TestOrchAvail *bool AIRunnerImage *string AIRunnerImageOverrides *string + AIVerboseLogs *bool + AIProcessingRetryTimeout *time.Duration KafkaBootstrapServers *string KafkaUsername *string KafkaPassword *string @@ -215,6 +217,8 @@ func DefaultLivepeerConfig() LivepeerConfig { defaultAIModels := "" defaultAIModelsDir := "" defaultAIRunnerImage := "livepeer/ai-runner:latest" + defaultAIVerboseLogs := false + defaultAIProcessingRetryTimeout := 2 * time.Second defaultAIRunnerImageOverrides := "" defaultLiveAIAuthWebhookURL := "" defaultLivePaymentInterval := 5 * time.Second @@ -320,15 +324,17 @@ func DefaultLivepeerConfig() LivepeerConfig { TestTranscoder: &defaultTestTranscoder, // AI: - AIServiceRegistry: &defaultAIServiceRegistry, - AIWorker: &defaultAIWorker, - AIModels: &defaultAIModels, - AIModelsDir: &defaultAIModelsDir, - AIRunnerImage: &defaultAIRunnerImage, - AIRunnerImageOverrides: &defaultAIRunnerImageOverrides, - LiveAIAuthWebhookURL: &defaultLiveAIAuthWebhookURL, - LivePaymentInterval: &defaultLivePaymentInterval, - GatewayHost: &defaultGatewayHost, + AIServiceRegistry: &defaultAIServiceRegistry, + AIWorker: &defaultAIWorker, + AIModels: &defaultAIModels, + AIModelsDir: &defaultAIModelsDir, + AIRunnerImage: &defaultAIRunnerImage, + AIVerboseLogs: &defaultAIVerboseLogs, + AIProcessingRetryTimeout: &defaultAIProcessingRetryTimeout, + AIRunnerImageOverrides: &defaultAIRunnerImageOverrides, + LiveAIAuthWebhookURL: &defaultLiveAIAuthWebhookURL, + LivePaymentInterval: &defaultLivePaymentInterval, + GatewayHost: &defaultGatewayHost, // Onchain: EthAcctAddr: &defaultEthAcctAddr, @@ -513,6 +519,7 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { if err != nil { glog.Errorf("Error creating livepeer node: %v", err) } + n.AIProcesssingRetryTimeout = *cfg.AIProcessingRetryTimeout if *cfg.OrchSecret != "" { n.OrchSecret, _ = common.ReadFromFile(*cfg.OrchSecret) @@ -1225,13 +1232,13 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { // Backwards compatibility for deprecated flags. if *cfg.AIRunnerImage != "" { - glog.Warning("-aiRunnerImage flag is deprecated and will be removed in a future release. Please use -aiWorkerImageOverrides instead") + glog.Warning("-aiRunnerImage flag is deprecated and will be removed in a future release. Please use -aiRunnerImageOverrides instead") if imageOverrides.Default == "" { imageOverrides.Default = *cfg.AIRunnerImage } } - n.AIWorker, err = worker.NewWorker(imageOverrides, gpus, modelsDir) + n.AIWorker, err = worker.NewWorker(imageOverrides, *cfg.AIVerboseLogs, gpus, modelsDir) if err != nil { glog.Errorf("Error starting AI worker: %v", err) return diff --git a/cmd/livepeer_cli/livepeer_cli.go b/cmd/livepeer_cli/livepeer_cli.go index c6ea22fd12..e73773cfdb 100644 --- a/cmd/livepeer_cli/livepeer_cli.go +++ b/cmd/livepeer_cli/livepeer_cli.go @@ -112,7 +112,8 @@ func (w *wizard) initializeOptions() []wizardOpt { }, testnet: true}, {desc: "Sign a message", invoke: w.signMessage}, {desc: "Sign typed data", invoke: w.signTypedData}, - {desc: "Vote in a poll", invoke: w.vote, orchestrator: true}, + {desc: "Vote in a governance poll", invoke: w.vote, orchestrator: true}, + {desc: "Vote on a treasury proposal", invoke: w.voteOnProposal, orchestrator: true}, {desc: "Set max ticket face value", invoke: w.setMaxFaceValue, orchestrator: true}, {desc: "Set price for broadcaster", invoke: w.setPriceForBroadcaster, orchestrator: true}, {desc: "Set maximum sessions", invoke: w.setMaxSessions, orchestrator: true, notOrchestrator: false}, diff --git a/cmd/livepeer_cli/wizard_token.go b/cmd/livepeer_cli/wizard_token.go index b72e1ca756..5da482c025 100644 --- a/cmd/livepeer_cli/wizard_token.go +++ b/cmd/livepeer_cli/wizard_token.go @@ -9,7 +9,7 @@ import ( func (w *wizard) transferTokens() { fmt.Printf("Current LPT balance: %v\n", w.getTokenBalance()) - fmt.Printf("Enter receipient address (in hex i.e. 0xfoo) - ") + fmt.Printf("Enter recipient address (in hex i.e. 0xfoo) - ") to := w.readString() amount := w.readBigInt("Enter amount") diff --git a/cmd/livepeer_cli/wizard_transcoder.go b/cmd/livepeer_cli/wizard_transcoder.go index 262a9b9140..66adce0917 100644 --- a/cmd/livepeer_cli/wizard_transcoder.go +++ b/cmd/livepeer_cli/wizard_transcoder.go @@ -278,6 +278,64 @@ func (w *wizard) vote() { fmt.Printf("\nVote success tx=0x%x\n", []byte(result)) } +func (w *wizard) voteOnProposal() { + if w.offchain { + glog.Error("Cannot vote in 'offchain' mode") + return + } + + fmt.Print("Enter the proposal ID you want to vote on -") + proposalID := w.readStringAndValidate(func(in string) (string, error) { + if _, ok := new(big.Int).SetString(in, 10); !ok { + return "", fmt.Errorf("invalid proposal ID id=%v", in) + } + return in, nil + }) + + var ( + confirm = "n" + choice = types.ProposalVoteChoice(-1) + ) + + for confirm == "n" { + w.showProposalVoteChoices() + + for { + fmt.Printf("Enter the ID of the choice you want to vote for -") + choice = types.ProposalVoteChoice(w.readInt()) + if choice.IsValid() { + break + } + fmt.Println("Must enter a valid ID") + } + + fmt.Printf("Are you sure you want to vote \"%v\"? (y/n) -", choice.String()) + confirm = w.readStringYesOrNo() + } + + fmt.Printf("Do you want to provide a reason for your vote? (y/n) -") + provideReason := w.readStringYesOrNo() + var reason string + if provideReason == "y" { + fmt.Print("Enter your reason -") + reason = w.readString() + } + + data := url.Values{ + "proposalID": {proposalID}, + "support": {fmt.Sprintf("%v", int(choice))}, + "reason": {reason}, + } + + result, ok := httpPostWithParams(fmt.Sprintf("http://%v:%v/voteOnProposal", w.host, w.httpPort), data) + + if !ok { + fmt.Printf("Error voting: %s\n", result) + return + } + fmt.Printf("\nVote success tx=0x%x\n", []byte(result)) +} + func (w *wizard) showVoteChoices() { wtr := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) fmt.Fprintln(wtr, "Identifier\tVoting Choices") @@ -287,6 +345,15 @@ func (w *wizard) showVoteChoices() { wtr.Flush() } +func (w *wizard) showProposalVoteChoices() { + wtr := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) + fmt.Fprintln(wtr, "Identifier\tVoting Choices") + for _, choice := range types.ProposalVoteChoices { + fmt.Fprintf(wtr, "%v\t%v\n", int(choice), choice.String()) + } + wtr.Flush() +} + func flipPerc(perc *big.Int) *big.Int { return new(big.Int).Sub(hundredPercent, perc) } diff --git a/common/types.go b/common/types.go index 925a2e6916..73fc0499fc 100644 --- a/common/types.go +++ b/common/types.go @@ -6,6 +6,7 @@ import ( "math/big" "net/url" "sync" + "time" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/livepeer/go-livepeer/net" @@ -58,8 +59,9 @@ const ( ) type OrchestratorLocalInfo struct { - URL *url.URL `json:"Url"` - Score float32 + URL *url.URL `json:"Url"` + Score float32 + Latency *time.Duration } // combines B's local metadata about O with info received from this O diff --git a/core/accounting.go b/core/accounting.go index ccb035b65d..f515b13fa1 100644 --- a/core/accounting.go +++ b/core/accounting.go @@ -75,7 +75,7 @@ func NewAddressBalances(ttl time.Duration) *AddressBalances { } } -// Credit adds an an amount to the balance for an address' ManifestID +// Credit adds an amount to the balance for an address' ManifestID func (a *AddressBalances) Credit(addr ethcommon.Address, id ManifestID, amount *big.Rat) { a.balancesForAddr(addr).Credit(id, amount) } @@ -142,7 +142,7 @@ func NewBalances(ttl time.Duration) *Balances { } } -// Credit adds an an amount to the balance for a ManifestID +// Credit adds an amount to the balance for a ManifestID func (b *Balances) Credit(id ManifestID, amount *big.Rat) { b.mtx.Lock() defer b.mtx.Unlock() diff --git a/core/ai.go b/core/ai.go index a30c6412ce..dfe0e79cef 100644 --- a/core/ai.go +++ b/core/ai.go @@ -12,7 +12,7 @@ import ( "strings" "github.com/golang/glog" - "github.com/livepeer/ai-worker/worker" + "github.com/livepeer/go-livepeer/ai/worker" ) var errPipelineNotAvailable = errors.New("pipeline not available") @@ -27,7 +27,7 @@ type AI interface { SegmentAnything2(context.Context, worker.GenSegmentAnything2MultipartRequestBody) (*worker.MasksResponse, error) ImageToText(context.Context, worker.GenImageToTextMultipartRequestBody) (*worker.ImageToTextResponse, error) TextToSpeech(context.Context, worker.GenTextToSpeechJSONRequestBody) (*worker.AudioResponse, error) - LiveVideoToVideo(context.Context, worker.GenLiveVideoToVideoJSONRequestBody) (*worker.LiveVideoToVideoResponse, error) + LiveVideoToVideo(context.Context, string, string, worker.GenLiveVideoToVideoJSONRequestBody) (*worker.LiveVideoToVideoResponse, error) Warm(context.Context, string, string, worker.RunnerEndpoint, worker.OptimizationFlags) error Stop(context.Context) error HasCapacity(string, string) bool diff --git a/core/ai_worker.go b/core/ai_orchestrator.go similarity index 97% rename from core/ai_worker.go rename to core/ai_orchestrator.go index 0e7aa4a424..0dd5520d73 100644 --- a/core/ai_worker.go +++ b/core/ai_orchestrator.go @@ -1,5 +1,7 @@ package core +// ai_orchestrator.go implements logic for managing AI workers and processing AI jobs. + import ( "bytes" "context" @@ -14,7 +16,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/golang/glog" - "github.com/livepeer/ai-worker/worker" + "github.com/livepeer/go-livepeer/ai/worker" "github.com/livepeer/go-livepeer/clog" "github.com/livepeer/go-livepeer/common" "github.com/livepeer/go-livepeer/monitor" @@ -531,7 +533,7 @@ func (orch *orchestrator) TextToImage(ctx context.Context, requestID string, req } } - // remote ai worker proceses job + // remote ai worker processes job res, err := orch.node.AIWorkerManager.Process(ctx, requestID, "text-to-image", *req.ModelId, "", AIJobRequestData{Request: req}) if err != nil { return nil, err @@ -549,10 +551,10 @@ func (orch *orchestrator) TextToImage(ctx context.Context, requestID string, req return res.Results, nil } -func (orch *orchestrator) LiveVideoToVideo(ctx context.Context, requestID string, req worker.GenLiveVideoToVideoJSONRequestBody) (interface{}, error) { +func (orch *orchestrator) LiveVideoToVideo(ctx context.Context, requestID, streamID string, req worker.GenLiveVideoToVideoJSONRequestBody) (interface{}, error) { // local AIWorker processes job if combined orchestrator/ai worker if orch.node.AIWorker != nil { - workerResp, err := orch.node.LiveVideoToVideo(ctx, req) + workerResp, err := orch.node.LiveVideoToVideo(ctx, requestID, streamID, req) if err == nil { return orch.node.saveLocalAIWorkerResults(ctx, *workerResp, requestID, "application/json") } else { @@ -597,7 +599,7 @@ func (orch *orchestrator) ImageToImage(ctx context.Context, requestID string, re } } - // remote ai worker proceses job + // remote ai worker processes job imgBytes, err := req.Image.Bytes() if err != nil { return nil, err @@ -641,7 +643,7 @@ func (orch *orchestrator) ImageToVideo(ctx context.Context, requestID string, re } } - // remote ai worker proceses job + // remote ai worker processes job imgBytes, err := req.Image.Bytes() if err != nil { return nil, err @@ -685,7 +687,7 @@ func (orch *orchestrator) Upscale(ctx context.Context, requestID string, req wor } } - // remote ai worker proceses job + // remote ai worker processes job imgBytes, err := req.Image.Bytes() if err != nil { return nil, err @@ -721,7 +723,7 @@ func (orch *orchestrator) AudioToText(ctx context.Context, requestID string, req return orch.node.AudioToText(ctx, req) } - // remote ai worker proceses job + // remote ai worker processes job audioBytes, err := req.Audio.Bytes() if err != nil { return nil, err @@ -757,7 +759,7 @@ func (orch *orchestrator) SegmentAnything2(ctx context.Context, requestID string return orch.node.SegmentAnything2(ctx, req) } - // remote ai worker proceses job + // remote ai worker processes job imgBytes, err := req.Image.Bytes() if err != nil { return nil, err @@ -822,7 +824,7 @@ func (orch *orchestrator) ImageToText(ctx context.Context, requestID string, req return orch.node.ImageToText(ctx, req) } - // remote ai worker proceses job + // remote ai worker processes job imageBytes, err := req.Image.Bytes() if err != nil { return nil, err @@ -866,7 +868,7 @@ func (orch *orchestrator) TextToSpeech(ctx context.Context, requestID string, re } } - // remote ai worker proceses job + // remote ai worker processes job res, err := orch.node.AIWorkerManager.Process(ctx, requestID, "text-to-speech", *req.ModelId, "", AIJobRequestData{Request: req}) if err != nil { return nil, err @@ -1058,8 +1060,8 @@ func (n *LivepeerNode) TextToSpeech(ctx context.Context, req worker.GenTextToSpe return n.AIWorker.TextToSpeech(ctx, req) } -func (n *LivepeerNode) LiveVideoToVideo(ctx context.Context, req worker.GenLiveVideoToVideoJSONRequestBody) (*worker.LiveVideoToVideoResponse, error) { - return n.AIWorker.LiveVideoToVideo(ctx, req) +func (n *LivepeerNode) LiveVideoToVideo(ctx context.Context, requestID, streamID string, req worker.GenLiveVideoToVideoJSONRequestBody) (*worker.LiveVideoToVideoResponse, error) { + return n.AIWorker.LiveVideoToVideo(ctx, requestID, streamID, req) } // transcodeFrames converts a series of image URLs into a video segment for the image-to-video pipeline. diff --git a/core/ai_test.go b/core/ai_test.go index 863dfcbb81..17e8b324f5 100644 --- a/core/ai_test.go +++ b/core/ai_test.go @@ -10,7 +10,7 @@ import ( "testing" "time" - "github.com/livepeer/ai-worker/worker" + "github.com/livepeer/go-livepeer/ai/worker" "github.com/livepeer/go-livepeer/common" "github.com/livepeer/go-livepeer/net" "github.com/livepeer/go-tools/drivers" @@ -666,7 +666,7 @@ func (a *stubAIWorker) TextToSpeech(ctx context.Context, req worker.GenTextToSpe return &worker.AudioResponse{Audio: worker.MediaURL{Url: "http://example.com/audio.wav"}}, nil } -func (a *stubAIWorker) LiveVideoToVideo(ctx context.Context, req worker.GenLiveVideoToVideoJSONRequestBody) (*worker.LiveVideoToVideoResponse, error) { +func (a *stubAIWorker) LiveVideoToVideo(ctx context.Context, requestID, streamID string, req worker.GenLiveVideoToVideoJSONRequestBody) (*worker.LiveVideoToVideoResponse, error) { return &worker.LiveVideoToVideoResponse{}, nil } diff --git a/core/autoconvertedprice_test.go b/core/autoconvertedprice_test.go index 54db6cfde0..dd3302b938 100644 --- a/core/autoconvertedprice_test.go +++ b/core/autoconvertedprice_test.go @@ -109,7 +109,7 @@ func TestAutoConvertedPrice_Update(t *testing.T) { 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 + require.Equal(big.NewRat(5e16, 6), price.Value()) // must also update current value case <-time.After(time.Second): t.Fatal("Expected price update not received") } diff --git a/core/capabilities.go b/core/capabilities.go index d2425fa988..5786b46d8c 100644 --- a/core/capabilities.go +++ b/core/capabilities.go @@ -98,7 +98,7 @@ var CapabilityNameLookup = map[Capability]string{ Capability_ProfileH264Baseline: "H264 Baseline profile", Capability_ProfileH264Main: "H264 Main profile", Capability_ProfileH264High: "H264 High profile", - Capability_ProfileH264ConstrainedHigh: "H264 Constained High profile", + Capability_ProfileH264ConstrainedHigh: "H264 Constrained, Contained High profile", Capability_GOP: "GOP", Capability_AuthToken: "Auth token", Capability_MPEG7VideoSignature: "MPEG7 signature", diff --git a/core/livepeernode.go b/core/livepeernode.go index 40cfa53fe4..0169d22bfb 100644 --- a/core/livepeernode.go +++ b/core/livepeernode.go @@ -119,8 +119,9 @@ type LivepeerNode struct { Database *common.DB // AI worker public fields - AIWorker AI - AIWorkerManager *RemoteAIWorkerManager + AIWorker AI + AIWorkerManager *RemoteAIWorkerManager + AIProcesssingRetryTimeout time.Duration // Transcoder public fields SegmentChans map[ManifestID]SegmentChan @@ -165,7 +166,6 @@ type LivepeerNode struct { type LivePipeline struct { ControlPub *trickle.TricklePublisher StopControl func() - EventsPub *trickle.TricklePublisher } // NewLivepeerNode creates a new Livepeer Node. Eth can be nil. diff --git a/core/orch_test.go b/core/orch_test.go index 72aa9cb8bb..44ae863011 100644 --- a/core/orch_test.go +++ b/core/orch_test.go @@ -400,7 +400,7 @@ func TestRemoveFromRemoteTranscoders(t *testing.T) { remoteTranscoderList = append(remoteTranscoderList, tr...) assert.Len(remoteTranscoderList, 5) - // Remove transcoder froms head of the list + // Remove transcoder forms head of the list remoteTranscoderList = removeFromRemoteTranscoders(tr[0], remoteTranscoderList) assert.Equal(remoteTranscoderList[0], tr[1]) assert.Equal(remoteTranscoderList[1], tr[2]) diff --git a/core/orchestrator.go b/core/orchestrator.go index 089b66a4da..240e390290 100644 --- a/core/orchestrator.go +++ b/core/orchestrator.go @@ -1087,7 +1087,7 @@ func (rtm *RemoteTranscoderManager) selectTranscoder(sessionId string, caps *Cap return nil, ErrNoTranscodersAvailable } - // Assinging transcoder to session for future use + // Assigning transcoder to session for future use rtm.streamSessions[sessionId] = currentTranscoder currentTranscoder.load++ sort.Sort(byLoadFactor(rtm.remoteTranscoders)) diff --git a/core/transcoder_test.go b/core/transcoder_test.go index a6d687504e..8a777a4ec8 100644 --- a/core/transcoder_test.go +++ b/core/transcoder_test.go @@ -97,7 +97,7 @@ func TestResToTranscodeData(t *testing.T) { _, err = resToTranscodeData(context.TODO(), res, opts) assert.EqualError(err, "open badfile: no such file or directory") - // Test error after a successful read + // Test error after a successsful read res = &ffmpeg.TranscodeResults{Encoded: make([]ffmpeg.MediaInfo, 3)} tempDir := t.TempDir() @@ -116,7 +116,7 @@ func TestResToTranscodeData(t *testing.T) { assert.True(fileDNE(file1.Name())) assert.False(fileDNE(file2.Name())) - // Test success for 1 output file + // Test successs for 1 output file res = &ffmpeg.TranscodeResults{Encoded: make([]ffmpeg.MediaInfo, 1)} res.Encoded[0].Pixels = 100 @@ -127,7 +127,7 @@ func TestResToTranscodeData(t *testing.T) { assert.Equal(int64(100), tData.Segments[0].Pixels) assert.True(fileDNE(file2.Name())) - // Test succes for 2 output files + // Test success for 2 output files res = &ffmpeg.TranscodeResults{Encoded: make([]ffmpeg.MediaInfo, 2)} res.Encoded[0].Pixels = 200 res.Encoded[1].Pixels = 300 diff --git a/discovery/db_discovery.go b/discovery/db_discovery.go index a1c657f229..eed12db1aa 100644 --- a/discovery/db_discovery.go +++ b/discovery/db_discovery.go @@ -189,7 +189,7 @@ func (dbo *DBOrchestratorPoolCache) cacheOrchestratorStake() error { } resc, errc := make(chan *common.DBOrch, len(orchs)), make(chan error, len(orchs)) - timeout := getOrchestratorTimeoutLoop //needs to be same or longer than GRPCConnectTimeout in server/rpc.go + timeout := getOrchestratorTimeoutLoop // Needs to be same or longer than GRPCConnectTimeout in server/rpc.go ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() @@ -266,7 +266,7 @@ func (dbo *DBOrchestratorPoolCache) cacheDBOrchs() error { } resc, errc := make(chan *common.DBOrch, len(orchs)), make(chan error, len(orchs)) - timeout := getOrchestratorTimeoutLoop //needs to be same or longer than GRPCConnectTimeout in server/rpc.go + timeout := getOrchestratorTimeoutLoop // Needs to be same or longer than GRPCConnectTimeout in server/rpc.go ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() @@ -276,7 +276,11 @@ func (dbo *DBOrchestratorPoolCache) cacheDBOrchs() error { errc <- err return } - + // Do not connect if URI host is not set + if uri.Host == "" { + errc <- fmt.Errorf("skipping orch=%v, URI not set", dbOrch.EthereumAddr) + return + } info, err := serverGetOrchInfo(ctx, dbo.bcast, uri, nil) if err != nil { errc <- err @@ -328,7 +332,7 @@ func (dbo *DBOrchestratorPoolCache) cacheDBOrchs() error { case err := <-errc: glog.Errorln(err) case <-ctx.Done(): - glog.Info("Done fetching orch info for orchestrators, context timeout") + glog.Infof("Done fetching orch info for orchestrators, context timeout (fetched: %v out of %v)", i, numOrchs) return nil } } diff --git a/discovery/discovery.go b/discovery/discovery.go index 9fe2631a63..4d82b0a495 100644 --- a/discovery/discovery.go +++ b/discovery/discovery.go @@ -8,9 +8,11 @@ import ( "math" "math/rand" "net/url" + "strconv" "strings" "time" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/livepeer/go-livepeer/clog" "github.com/livepeer/go-livepeer/common" "github.com/livepeer/go-livepeer/monitor" @@ -108,14 +110,21 @@ func (o *orchestratorPool) GetOrchestrators(ctx context.Context, numOrchestrator getOrchInfo := func(ctx context.Context, od common.OrchestratorDescriptor, infoCh chan common.OrchestratorDescriptor, errCh chan error) { start := time.Now() info, err := serverGetOrchInfo(ctx, o.bcast, od.LocalInfo.URL, caps.ToNetCapabilities()) - clog.V(common.DEBUG).Infof(ctx, "Received GetOrchInfo RPC Response from uri=%v, latency=%v", od.LocalInfo.URL, time.Since(start)) + latency := time.Since(start) + clog.V(common.DEBUG).Infof(ctx, "Received GetOrchInfo RPC Response from uri=%v, latency=%v", od.LocalInfo.URL, latency) if err == nil && !isBlacklisted(info) && isCompatible(info) { - od.RemoteInfo = info - infoCh <- od + infoCh <- common.OrchestratorDescriptor{ + LocalInfo: &common.OrchestratorLocalInfo{ + URL: od.LocalInfo.URL, + Score: od.LocalInfo.Score, + Latency: &latency, + }, + RemoteInfo: info, + } return } + clog.V(common.DEBUG).Infof(ctx, "Discovery unsuccessful for orchestrator %s, err=%q", od.LocalInfo.URL.String(), err) if err != nil && !errors.Is(err, context.Canceled) { - clog.V(common.DEBUG).Infof(ctx, "err=%q", err) if monitor.Enabled { monitor.LogDiscoveryError(ctx, od.LocalInfo.URL.String(), err.Error()) } @@ -179,6 +188,17 @@ func (o *orchestratorPool) GetOrchestrators(ctx context.Context, numOrchestrator } } + if monitor.Enabled && len(ods) > 0 { + var discoveryResults []map[string]string + for _, o := range ods { + discoveryResults = append(discoveryResults, map[string]string{ + "address": hexutil.Encode(o.RemoteInfo.Address), + "url": o.RemoteInfo.Transcoder, + "latency_ms": strconv.FormatInt(o.LocalInfo.Latency.Milliseconds(), 10), + }) + } + monitor.SendQueueEventAsync("discovery_results", discoveryResults) + } clog.Infof(ctx, "Done fetching orch info numOrch=%d responses=%d/%d timedOut=%t", len(ods), nbResp, len(linfos), timedOut) return ods, nil diff --git a/discovery/discovery_test.go b/discovery/discovery_test.go index 8ffc2fe013..0593d3eb12 100644 --- a/discovery/discovery_test.go +++ b/discovery/discovery_test.go @@ -1138,7 +1138,7 @@ func TestNewWHOrchestratorPoolCache(t *testing.T) { for _, addr := range addresses { uri, _ := url.ParseRequestURI(addr) - assert.Contains(infos, common.OrchestratorLocalInfo{URL: uri}) + assert.Contains(removeLatency(infos), common.OrchestratorLocalInfo{URL: uri, Latency: nil}) } // assert that list is not refreshed if lastRequest is more than 1 min ago and hash is the same @@ -1158,7 +1158,7 @@ func TestNewWHOrchestratorPoolCache(t *testing.T) { for _, addr := range addresses { uri, _ := url.ParseRequestURI(addr) - assert.Contains(infos, common.OrchestratorLocalInfo{URL: uri}) + assert.Contains(removeLatency(infos), common.OrchestratorLocalInfo{URL: uri, Latency: nil}) } // mock a change in webhook addresses @@ -1181,7 +1181,7 @@ func TestNewWHOrchestratorPoolCache(t *testing.T) { for _, addr := range addresses { uri, _ := url.ParseRequestURI(addr) - assert.NotContains(infos, common.OrchestratorLocalInfo{URL: uri}) + assert.NotContains(removeLatency(infos), common.OrchestratorLocalInfo{URL: uri, Latency: nil}) } // assert that list is refreshed if lastRequest is longer than 1 min ago and hash is not the same @@ -1201,7 +1201,7 @@ func TestNewWHOrchestratorPoolCache(t *testing.T) { for _, addr := range addresses { uri, _ := url.ParseRequestURI(addr) - assert.Contains(infos, common.OrchestratorLocalInfo{URL: uri}) + assert.Contains(removeLatency(infos), common.OrchestratorLocalInfo{URL: uri, Latency: nil}) } } @@ -1649,3 +1649,12 @@ func TestSetGetOrchestratorTimeout(t *testing.T) { assert.Equal(poolCache.discoveryTimeout, 1000*time.Millisecond) } + +func removeLatency(infos []common.OrchestratorLocalInfo) []common.OrchestratorLocalInfo { + var res []common.OrchestratorLocalInfo + for _, i := range infos { + i.Latency = nil + res = append(res, i) + } + return res +} diff --git a/doc/assets/ai-runner-container-lifecycle.jpg b/doc/assets/ai-runner-container-lifecycle.jpg new file mode 100644 index 0000000000..6e9f0f3c8b Binary files /dev/null and b/doc/assets/ai-runner-container-lifecycle.jpg differ diff --git a/doc/worker.md b/doc/worker.md new file mode 100644 index 0000000000..985a079aab --- /dev/null +++ b/doc/worker.md @@ -0,0 +1,31 @@ +# AI Worker + +The AI Worker node manages [`ai-runner`](https://github.com/livepeer/ai-runner) containers running on the host system. +These containers are started, monitored and stopped dynamically depending on the usage. + +This diagram describes the lifecycle of a container: + +![ai-runner container lifecycle](./assets/ai-runner-container-lifecycle.jpg) + +Source: [Miro Board](https://miro.com/app/board/uXjVIZ0vO4k=/?share_link_id=987855784886) + +It can also be described by the following mermaid chart, but the rendered version is more confusing: +``` +stateDiagram-v2 + direction TB + [*] --> OFFLINE + OFFLINE --> IDLE: Warm()->createCont() + OFFLINE --> BORROWED: Borrow(ctx)->createCont() + state RUNNING { + [*] --> IDLE + IDLE --> BORROWED: Borrow(ctx) + BORROWED --> IDLE: BorrowCtx.Done() + } + hc: GET /health + RUNNING --> hc + state healthcheck <> + hc --> healthcheck + healthcheck --> OFFLINE: if error x2 + healthcheck --> RUNNING: if state=OK + healthcheck --> IDLE: if state=IDLE +``` diff --git a/docker/Dockerfile b/docker/Dockerfile index d9a2635917..993955f1ba 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -12,14 +12,14 @@ ENV GOARCH="$TARGETARCH" \ CGO_LDFLAGS="-L/usr/local/cuda_${TARGETARCH}/lib64" RUN apt update \ - && apt install -yqq software-properties-common curl apt-transport-https lsb-release yasm \ + && apt install -yqq software-properties-common curl apt-transport-https lsb-release nasm \ && 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 - \ && 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 zlib1g zlib1g-dev + && 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 libx264-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 \ @@ -30,6 +30,11 @@ RUN GRPC_HEALTH_PROBE_VERSION=v0.3.6 \ && chmod +x /usr/bin/grpc_health_probe \ && ldconfig /usr/local/lib +RUN FFMPEG_SHA=b76053d8bf322b197a9d07bd27bbdad14fd5bc15 git clone --depth 1 https://git.ffmpeg.org/ffmpeg.git /ffmpeg \ + && cd /ffmpeg && git fetch --depth 1 origin ${FFMPEG_SHA} \ + && git checkout ${FFMPEG_SHA} \ + && ./configure --enable-gpl --enable-libx264 --prefix=build && make -j"$(nproc)" && make install + ENV GOPATH=/go \ GO_BUILD_DIR=/build/ \ GOFLAGS="-mod=readonly" @@ -63,11 +68,13 @@ FROM livepeer-${TARGETARCH}-base ENV NVIDIA_DRIVER_CAPABILITIES=all +RUN apt update && apt install -y libx264-155 + COPY --from=build /build/ /usr/local/bin/ COPY --from=build /usr/bin/grpc_health_probe /usr/local/bin/grpc_health_probe COPY --from=build /src/tasmodel.pb /tasmodel.pb COPY --from=build /usr/share/misc/pci.ids /usr/share/misc/pci.ids - -RUN apt update && apt install -yqq ffmpeg +COPY --from=build /ffmpeg/build/ /usr/local +RUN ldconfig /usr/local/lib ENTRYPOINT ["/usr/local/bin/livepeer"] diff --git a/docker/Dockerfile.mediamtx b/docker/Dockerfile.mediamtx index 80abe0dcc6..b51cabae9e 100644 --- a/docker/Dockerfile.mediamtx +++ b/docker/Dockerfile.mediamtx @@ -1,3 +1,27 @@ +ARG MEDIAMTX_VERSION="1.11.2-livepeer-2" + +FROM golang:1.23 AS builder + +# Install any build dependencies (e.g., git) +RUN apt-get update && apt-get install -y --no-install-recommends \ + git \ + && rm -rf /var/lib/apt/lists/* + +# Use this branch while we have changes waiting for upstream +ARG MEDIAMTX_VERSION +WORKDIR /app +RUN git clone --branch v${MEDIAMTX_VERSION} https://github.com/livepeer/mediamtx.git . + +# Download Go dependencies +RUN go mod download + +# Disable CGO +ENV CGO_ENABLED=0 + +# Generate code and build +RUN go generate ./... +RUN go build -o mediamtx + FROM ubuntu:24.04 # we need curl in the image as it's later used in the runOnReady command @@ -18,14 +42,10 @@ RUN mkdir -p /var/log/ \ COPY --chmod=0755 mediamtx-metrics.bash /opt/mediamtx-metrics.bash -ENV MEDIAMTX_VERSION="1.9.3" - -ADD "https://github.com/bluenviron/mediamtx/releases/download/v${MEDIAMTX_VERSION}/mediamtx_v${MEDIAMTX_VERSION}_linux_amd64.tar.gz" /opt/mediamtx/mediamtx.tar.gz - -RUN tar xzf /opt/mediamtx/mediamtx.tar.gz -C /opt/mediamtx/ \ - && mkdir -p /usr/local/bin /etc/mediamtx/ \ - && mv /opt/mediamtx/mediamtx /usr/local/bin/mediamtx \ - && mv /opt/mediamtx/mediamtx.yml /etc/mediamtx/mediamtx.yml \ - && rm -rf /opt/mediamtx/ +# Copy artifacts from the builder stage +ARG MEDIAMTX_VERSION +ENV MEDIAMTX_VERSION=${MEDIAMTX_VERSION} +COPY --from=builder /app/mediamtx /usr/local/bin/mediamtx +COPY --from=builder /app/mediamtx.yml /etc/mediamtx/mediamtx.yml CMD [ "/bin/bash", "-c", "declare -p >> /etc/environment && cron && /usr/local/bin/mediamtx" ] diff --git a/eth/client.go b/eth/client.go index 1ed4cc1e46..3390f79a8b 100644 --- a/eth/client.go +++ b/eth/client.go @@ -12,6 +12,7 @@ package eth //go:generate abigen --abi protocol/abi/Minter.json --pkg contracts --type Minter --out contracts/minter.go //go:generate abigen --abi protocol/abi/LivepeerTokenFaucet.json --pkg contracts --type LivepeerTokenFaucet --out contracts/livepeerTokenFaucet.go //go:generate abigen --abi protocol/abi/Poll.json --pkg contracts --type Poll --out contracts/poll.go +//go:generate abigen --abi protocol/abi/LivepeerGovernor.json --pkg contracts --type Governor --out contracts/LivepeerGovernor.go import ( "context" "fmt" @@ -113,6 +114,8 @@ type LivepeerEthClient interface { // Governance Vote(ethcommon.Address, *big.Int) (*types.Transaction, error) + ProposalVote(*big.Int, uint8) (*types.Transaction, error) + ProposalVoteWithReason(*big.Int, uint8, string) (*types.Transaction, error) // Helpers ContractAddresses() map[string]ethcommon.Address @@ -130,15 +133,16 @@ type client struct { transOpts bind.TransactOpts transOptsMu sync.RWMutex - controllerAddr ethcommon.Address - tokenAddr ethcommon.Address - serviceRegistryAddr ethcommon.Address - bondingManagerAddr ethcommon.Address - ticketBrokerAddr ethcommon.Address - roundsManagerAddr ethcommon.Address - minterAddr ethcommon.Address - verifierAddr ethcommon.Address - faucetAddr ethcommon.Address + controllerAddr ethcommon.Address + tokenAddr ethcommon.Address + serviceRegistryAddr ethcommon.Address + bondingManagerAddr ethcommon.Address + ticketBrokerAddr ethcommon.Address + roundsManagerAddr ethcommon.Address + minterAddr ethcommon.Address + verifierAddr ethcommon.Address + faucetAddr ethcommon.Address + livepeerGovernorAddr ethcommon.Address // Contracts controller *contracts.Controller @@ -149,6 +153,7 @@ type client struct { roundsManager *contracts.RoundsManager minter *contracts.Minter livepeerTokenFaucet *contracts.LivepeerTokenFaucet + livepeerGovernor *contracts.Governor // for L1 contracts backwards-compatibility l1BondingManager *contracts.L1BondingManager @@ -325,6 +330,23 @@ func (c *client) setContracts(opts *bind.TransactOpts) error { glog.V(common.SHORT).Infof("LivepeerTokenFaucet: %v", c.faucetAddr.Hex()) + livepeerGovernorAddr, err := c.GetContract(crypto.Keccak256Hash([]byte("LivepeerGovernor"))) + if err != nil { + glog.Errorf("Error getting Governor address: %v", err) + return err + } + + c.livepeerGovernorAddr = livepeerGovernorAddr + + governor, err := contracts.NewGovernor(livepeerGovernorAddr, c.backend) + if err != nil { + glog.Errorf("Error creating Governor binding: %v", err) + return err + } + c.livepeerGovernor = governor + + glog.V(common.SHORT).Infof("LivepeerGovernor: %v", c.livepeerGovernorAddr.Hex()) + return nil } @@ -990,6 +1012,14 @@ func (c *client) Vote(pollAddr ethcommon.Address, choiceID *big.Int) (*types.Tra return poll.Vote(opts, choiceID) } +func (c *client) ProposalVote(proposalId *big.Int, support uint8) (*types.Transaction, error) { + return c.livepeerGovernor.CastVote(c.transactOpts(), proposalId, support) +} + +func (c *client) ProposalVoteWithReason(proposalId *big.Int, support uint8, reason string) (*types.Transaction, error) { + return c.livepeerGovernor.CastVoteWithReason(c.transactOpts(), proposalId, support, reason) +} + func (c *client) Reward() (*types.Transaction, error) { addr := c.accountManager.Account().Address diff --git a/eth/contracts/LivepeerGovernor.go b/eth/contracts/LivepeerGovernor.go new file mode 100644 index 0000000000..964dbcde23 --- /dev/null +++ b/eth/contracts/LivepeerGovernor.go @@ -0,0 +1,3745 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package contracts + +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 +) + +// GovernorMetaData contains all meta data concerning the Governor contract. +var GovernorMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_controller\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"Empty\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"voteType\",\"type\":\"uint8\"}],\"name\":\"InvalidVoteType\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"VoteAlreadyCast\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"EIP712DomainChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"param\",\"type\":\"string\"}],\"name\":\"ParameterUpdate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"}],\"name\":\"ProposalCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"proposer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"indexed\":false,\"internalType\":\"string[]\",\"name\":\"signatures\",\"type\":\"string[]\"},{\"indexed\":false,\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"voteStart\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"voteEnd\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"description\",\"type\":\"string\"}],\"name\":\"ProposalCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"}],\"name\":\"ProposalExecuted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"eta\",\"type\":\"uint256\"}],\"name\":\"ProposalQueued\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldProposalThreshold\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newProposalThreshold\",\"type\":\"uint256\"}],\"name\":\"ProposalThresholdSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldQuorumNumerator\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newQuorumNumerator\",\"type\":\"uint256\"}],\"name\":\"QuorumNumeratorUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"controller\",\"type\":\"address\"}],\"name\":\"SetController\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldTimelock\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newTimelock\",\"type\":\"address\"}],\"name\":\"TimelockChange\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"voter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"support\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"weight\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"reason\",\"type\":\"string\"}],\"name\":\"VoteCast\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"voter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"support\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"weight\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"reason\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"params\",\"type\":\"bytes\"}],\"name\":\"VoteCastWithParams\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldVotingDelay\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newVotingDelay\",\"type\":\"uint256\"}],\"name\":\"VotingDelaySet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldVotingPeriod\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newVotingPeriod\",\"type\":\"uint256\"}],\"name\":\"VotingPeriodSet\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BALLOT_TYPEHASH\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"CLOCK_MODE\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"COUNTING_MODE\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"EXTENDED_BALLOT_TYPEHASH\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"bumpGovernorVotesTokenAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes32\",\"name\":\"descriptionHash\",\"type\":\"bytes32\"}],\"name\":\"cancel\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"support\",\"type\":\"uint8\"}],\"name\":\"castVote\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"support\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"castVoteBySig\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"support\",\"type\":\"uint8\"},{\"internalType\":\"string\",\"name\":\"reason\",\"type\":\"string\"}],\"name\":\"castVoteWithReason\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"support\",\"type\":\"uint8\"},{\"internalType\":\"string\",\"name\":\"reason\",\"type\":\"string\"},{\"internalType\":\"bytes\",\"name\":\"params\",\"type\":\"bytes\"}],\"name\":\"castVoteWithReasonAndParams\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"support\",\"type\":\"uint8\"},{\"internalType\":\"string\",\"name\":\"reason\",\"type\":\"string\"},{\"internalType\":\"bytes\",\"name\":\"params\",\"type\":\"bytes\"},{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"castVoteWithReasonAndParamsBySig\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"clock\",\"outputs\":[{\"internalType\":\"uint48\",\"name\":\"\",\"type\":\"uint48\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"controller\",\"outputs\":[{\"internalType\":\"contractIController\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"eip712Domain\",\"outputs\":[{\"internalType\":\"bytes1\",\"name\":\"fields\",\"type\":\"bytes1\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"version\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"verifyingContract\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"salt\",\"type\":\"bytes32\"},{\"internalType\":\"uint256[]\",\"name\":\"extensions\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes32\",\"name\":\"descriptionHash\",\"type\":\"bytes32\"}],\"name\":\"execute\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"timepoint\",\"type\":\"uint256\"}],\"name\":\"getVotes\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"timepoint\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"params\",\"type\":\"bytes\"}],\"name\":\"getVotesWithParams\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_proposalId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_account\",\"type\":\"address\"}],\"name\":\"hasVoted\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes32\",\"name\":\"descriptionHash\",\"type\":\"bytes32\"}],\"name\":\"hashProposal\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"initialVotingDelay\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"initialVotingPeriod\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"initialProposalThreshold\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"initialQuorum\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"quota\",\"type\":\"uint256\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"onERC1155BatchReceived\",\"outputs\":[{\"internalType\":\"bytes4\",\"name\":\"\",\"type\":\"bytes4\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"onERC1155Received\",\"outputs\":[{\"internalType\":\"bytes4\",\"name\":\"\",\"type\":\"bytes4\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"onERC721Received\",\"outputs\":[{\"internalType\":\"bytes4\",\"name\":\"\",\"type\":\"bytes4\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"}],\"name\":\"proposalDeadline\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"}],\"name\":\"proposalEta\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"}],\"name\":\"proposalProposer\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"}],\"name\":\"proposalSnapshot\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"proposalThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_proposalId\",\"type\":\"uint256\"}],\"name\":\"proposalVotes\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"againstVotes\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"forVotes\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"abstainVotes\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"},{\"internalType\":\"string\",\"name\":\"description\",\"type\":\"string\"}],\"name\":\"propose\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes32\",\"name\":\"descriptionHash\",\"type\":\"bytes32\"}],\"name\":\"queue\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"timepoint\",\"type\":\"uint256\"}],\"name\":\"quorum\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"quorumDenominator\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"timepoint\",\"type\":\"uint256\"}],\"name\":\"quorumNumerator\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"quorumNumerator\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"quota\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"relay\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_controller\",\"type\":\"address\"}],\"name\":\"setController\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newProposalThreshold\",\"type\":\"uint256\"}],\"name\":\"setProposalThreshold\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newVotingDelay\",\"type\":\"uint256\"}],\"name\":\"setVotingDelay\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newVotingPeriod\",\"type\":\"uint256\"}],\"name\":\"setVotingPeriod\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"}],\"name\":\"state\",\"outputs\":[{\"internalType\":\"enumIGovernorUpgradeable.ProposalState\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"targetContractId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"timelock\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"token\",\"outputs\":[{\"internalType\":\"contractIERC5805Upgradeable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newQuorumNumerator\",\"type\":\"uint256\"}],\"name\":\"updateQuorumNumerator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contractTimelockControllerUpgradeable\",\"name\":\"newTimelock\",\"type\":\"address\"}],\"name\":\"updateTimelock\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"version\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"votes\",\"outputs\":[{\"internalType\":\"contractIVotes\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"votingDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"votingPeriod\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]", +} + +// GovernorABI is the input ABI used to generate the binding from. +// Deprecated: Use GovernorMetaData.ABI instead. +var GovernorABI = GovernorMetaData.ABI + +// Governor is an auto generated Go binding around an Ethereum contract. +type Governor struct { + GovernorCaller // Read-only binding to the contract + GovernorTransactor // Write-only binding to the contract + GovernorFilterer // Log filterer for contract events +} + +// GovernorCaller is an auto generated read-only Go binding around an Ethereum contract. +type GovernorCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// GovernorTransactor is an auto generated write-only Go binding around an Ethereum contract. +type GovernorTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// GovernorFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type GovernorFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// GovernorSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type GovernorSession struct { + Contract *Governor // 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 +} + +// GovernorCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type GovernorCallerSession struct { + Contract *GovernorCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// GovernorTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type GovernorTransactorSession struct { + Contract *GovernorTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// GovernorRaw is an auto generated low-level Go binding around an Ethereum contract. +type GovernorRaw struct { + Contract *Governor // Generic contract binding to access the raw methods on +} + +// GovernorCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type GovernorCallerRaw struct { + Contract *GovernorCaller // Generic read-only contract binding to access the raw methods on +} + +// GovernorTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type GovernorTransactorRaw struct { + Contract *GovernorTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewGovernor creates a new instance of Governor, bound to a specific deployed contract. +func NewGovernor(address common.Address, backend bind.ContractBackend) (*Governor, error) { + contract, err := bindGovernor(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &Governor{GovernorCaller: GovernorCaller{contract: contract}, GovernorTransactor: GovernorTransactor{contract: contract}, GovernorFilterer: GovernorFilterer{contract: contract}}, nil +} + +// NewGovernorCaller creates a new read-only instance of Governor, bound to a specific deployed contract. +func NewGovernorCaller(address common.Address, caller bind.ContractCaller) (*GovernorCaller, error) { + contract, err := bindGovernor(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &GovernorCaller{contract: contract}, nil +} + +// NewGovernorTransactor creates a new write-only instance of Governor, bound to a specific deployed contract. +func NewGovernorTransactor(address common.Address, transactor bind.ContractTransactor) (*GovernorTransactor, error) { + contract, err := bindGovernor(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &GovernorTransactor{contract: contract}, nil +} + +// NewGovernorFilterer creates a new log filterer instance of Governor, bound to a specific deployed contract. +func NewGovernorFilterer(address common.Address, filterer bind.ContractFilterer) (*GovernorFilterer, error) { + contract, err := bindGovernor(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &GovernorFilterer{contract: contract}, nil +} + +// bindGovernor binds a generic wrapper to an already deployed contract. +func bindGovernor(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := GovernorMetaData.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 (_Governor *GovernorRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Governor.Contract.GovernorCaller.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 (_Governor *GovernorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Governor.Contract.GovernorTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Governor *GovernorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Governor.Contract.GovernorTransactor.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 (_Governor *GovernorCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Governor.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 (_Governor *GovernorTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Governor.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Governor *GovernorTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Governor.Contract.contract.Transact(opts, method, params...) +} + +// BALLOTTYPEHASH is a free data retrieval call binding the contract method 0xdeaaa7cc. +// +// Solidity: function BALLOT_TYPEHASH() view returns(bytes32) +func (_Governor *GovernorCaller) BALLOTTYPEHASH(opts *bind.CallOpts) ([32]byte, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "BALLOT_TYPEHASH") + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// BALLOTTYPEHASH is a free data retrieval call binding the contract method 0xdeaaa7cc. +// +// Solidity: function BALLOT_TYPEHASH() view returns(bytes32) +func (_Governor *GovernorSession) BALLOTTYPEHASH() ([32]byte, error) { + return _Governor.Contract.BALLOTTYPEHASH(&_Governor.CallOpts) +} + +// BALLOTTYPEHASH is a free data retrieval call binding the contract method 0xdeaaa7cc. +// +// Solidity: function BALLOT_TYPEHASH() view returns(bytes32) +func (_Governor *GovernorCallerSession) BALLOTTYPEHASH() ([32]byte, error) { + return _Governor.Contract.BALLOTTYPEHASH(&_Governor.CallOpts) +} + +// CLOCKMODE is a free data retrieval call binding the contract method 0x4bf5d7e9. +// +// Solidity: function CLOCK_MODE() view returns(string) +func (_Governor *GovernorCaller) CLOCKMODE(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "CLOCK_MODE") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// CLOCKMODE is a free data retrieval call binding the contract method 0x4bf5d7e9. +// +// Solidity: function CLOCK_MODE() view returns(string) +func (_Governor *GovernorSession) CLOCKMODE() (string, error) { + return _Governor.Contract.CLOCKMODE(&_Governor.CallOpts) +} + +// CLOCKMODE is a free data retrieval call binding the contract method 0x4bf5d7e9. +// +// Solidity: function CLOCK_MODE() view returns(string) +func (_Governor *GovernorCallerSession) CLOCKMODE() (string, error) { + return _Governor.Contract.CLOCKMODE(&_Governor.CallOpts) +} + +// COUNTINGMODE is a free data retrieval call binding the contract method 0xdd4e2ba5. +// +// Solidity: function COUNTING_MODE() pure returns(string) +func (_Governor *GovernorCaller) COUNTINGMODE(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "COUNTING_MODE") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// COUNTINGMODE is a free data retrieval call binding the contract method 0xdd4e2ba5. +// +// Solidity: function COUNTING_MODE() pure returns(string) +func (_Governor *GovernorSession) COUNTINGMODE() (string, error) { + return _Governor.Contract.COUNTINGMODE(&_Governor.CallOpts) +} + +// COUNTINGMODE is a free data retrieval call binding the contract method 0xdd4e2ba5. +// +// Solidity: function COUNTING_MODE() pure returns(string) +func (_Governor *GovernorCallerSession) COUNTINGMODE() (string, error) { + return _Governor.Contract.COUNTINGMODE(&_Governor.CallOpts) +} + +// EXTENDEDBALLOTTYPEHASH is a free data retrieval call binding the contract method 0x2fe3e261. +// +// Solidity: function EXTENDED_BALLOT_TYPEHASH() view returns(bytes32) +func (_Governor *GovernorCaller) EXTENDEDBALLOTTYPEHASH(opts *bind.CallOpts) ([32]byte, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "EXTENDED_BALLOT_TYPEHASH") + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// EXTENDEDBALLOTTYPEHASH is a free data retrieval call binding the contract method 0x2fe3e261. +// +// Solidity: function EXTENDED_BALLOT_TYPEHASH() view returns(bytes32) +func (_Governor *GovernorSession) EXTENDEDBALLOTTYPEHASH() ([32]byte, error) { + return _Governor.Contract.EXTENDEDBALLOTTYPEHASH(&_Governor.CallOpts) +} + +// EXTENDEDBALLOTTYPEHASH is a free data retrieval call binding the contract method 0x2fe3e261. +// +// Solidity: function EXTENDED_BALLOT_TYPEHASH() view returns(bytes32) +func (_Governor *GovernorCallerSession) EXTENDEDBALLOTTYPEHASH() ([32]byte, error) { + return _Governor.Contract.EXTENDEDBALLOTTYPEHASH(&_Governor.CallOpts) +} + +// Clock is a free data retrieval call binding the contract method 0x91ddadf4. +// +// Solidity: function clock() view returns(uint48) +func (_Governor *GovernorCaller) Clock(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "clock") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Clock is a free data retrieval call binding the contract method 0x91ddadf4. +// +// Solidity: function clock() view returns(uint48) +func (_Governor *GovernorSession) Clock() (*big.Int, error) { + return _Governor.Contract.Clock(&_Governor.CallOpts) +} + +// Clock is a free data retrieval call binding the contract method 0x91ddadf4. +// +// Solidity: function clock() view returns(uint48) +func (_Governor *GovernorCallerSession) Clock() (*big.Int, error) { + return _Governor.Contract.Clock(&_Governor.CallOpts) +} + +// Controller is a free data retrieval call binding the contract method 0xf77c4791. +// +// Solidity: function controller() view returns(address) +func (_Governor *GovernorCaller) Controller(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "controller") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Controller is a free data retrieval call binding the contract method 0xf77c4791. +// +// Solidity: function controller() view returns(address) +func (_Governor *GovernorSession) Controller() (common.Address, error) { + return _Governor.Contract.Controller(&_Governor.CallOpts) +} + +// Controller is a free data retrieval call binding the contract method 0xf77c4791. +// +// Solidity: function controller() view returns(address) +func (_Governor *GovernorCallerSession) Controller() (common.Address, error) { + return _Governor.Contract.Controller(&_Governor.CallOpts) +} + +// Eip712Domain is a free data retrieval call binding the contract method 0x84b0196e. +// +// Solidity: function eip712Domain() view returns(bytes1 fields, string name, string version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] extensions) +func (_Governor *GovernorCaller) Eip712Domain(opts *bind.CallOpts) (struct { + Fields [1]byte + Name string + Version string + ChainId *big.Int + VerifyingContract common.Address + Salt [32]byte + Extensions []*big.Int +}, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "eip712Domain") + + outstruct := new(struct { + Fields [1]byte + Name string + Version string + ChainId *big.Int + VerifyingContract common.Address + Salt [32]byte + Extensions []*big.Int + }) + if err != nil { + return *outstruct, err + } + + outstruct.Fields = *abi.ConvertType(out[0], new([1]byte)).(*[1]byte) + outstruct.Name = *abi.ConvertType(out[1], new(string)).(*string) + outstruct.Version = *abi.ConvertType(out[2], new(string)).(*string) + outstruct.ChainId = *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + outstruct.VerifyingContract = *abi.ConvertType(out[4], new(common.Address)).(*common.Address) + outstruct.Salt = *abi.ConvertType(out[5], new([32]byte)).(*[32]byte) + outstruct.Extensions = *abi.ConvertType(out[6], new([]*big.Int)).(*[]*big.Int) + + return *outstruct, err + +} + +// Eip712Domain is a free data retrieval call binding the contract method 0x84b0196e. +// +// Solidity: function eip712Domain() view returns(bytes1 fields, string name, string version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] extensions) +func (_Governor *GovernorSession) Eip712Domain() (struct { + Fields [1]byte + Name string + Version string + ChainId *big.Int + VerifyingContract common.Address + Salt [32]byte + Extensions []*big.Int +}, error) { + return _Governor.Contract.Eip712Domain(&_Governor.CallOpts) +} + +// Eip712Domain is a free data retrieval call binding the contract method 0x84b0196e. +// +// Solidity: function eip712Domain() view returns(bytes1 fields, string name, string version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] extensions) +func (_Governor *GovernorCallerSession) Eip712Domain() (struct { + Fields [1]byte + Name string + Version string + ChainId *big.Int + VerifyingContract common.Address + Salt [32]byte + Extensions []*big.Int +}, error) { + return _Governor.Contract.Eip712Domain(&_Governor.CallOpts) +} + +// GetVotes is a free data retrieval call binding the contract method 0xeb9019d4. +// +// Solidity: function getVotes(address account, uint256 timepoint) view returns(uint256) +func (_Governor *GovernorCaller) GetVotes(opts *bind.CallOpts, account common.Address, timepoint *big.Int) (*big.Int, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "getVotes", account, timepoint) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetVotes is a free data retrieval call binding the contract method 0xeb9019d4. +// +// Solidity: function getVotes(address account, uint256 timepoint) view returns(uint256) +func (_Governor *GovernorSession) GetVotes(account common.Address, timepoint *big.Int) (*big.Int, error) { + return _Governor.Contract.GetVotes(&_Governor.CallOpts, account, timepoint) +} + +// GetVotes is a free data retrieval call binding the contract method 0xeb9019d4. +// +// Solidity: function getVotes(address account, uint256 timepoint) view returns(uint256) +func (_Governor *GovernorCallerSession) GetVotes(account common.Address, timepoint *big.Int) (*big.Int, error) { + return _Governor.Contract.GetVotes(&_Governor.CallOpts, account, timepoint) +} + +// GetVotesWithParams is a free data retrieval call binding the contract method 0x9a802a6d. +// +// Solidity: function getVotesWithParams(address account, uint256 timepoint, bytes params) view returns(uint256) +func (_Governor *GovernorCaller) GetVotesWithParams(opts *bind.CallOpts, account common.Address, timepoint *big.Int, params []byte) (*big.Int, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "getVotesWithParams", account, timepoint, params) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetVotesWithParams is a free data retrieval call binding the contract method 0x9a802a6d. +// +// Solidity: function getVotesWithParams(address account, uint256 timepoint, bytes params) view returns(uint256) +func (_Governor *GovernorSession) GetVotesWithParams(account common.Address, timepoint *big.Int, params []byte) (*big.Int, error) { + return _Governor.Contract.GetVotesWithParams(&_Governor.CallOpts, account, timepoint, params) +} + +// GetVotesWithParams is a free data retrieval call binding the contract method 0x9a802a6d. +// +// Solidity: function getVotesWithParams(address account, uint256 timepoint, bytes params) view returns(uint256) +func (_Governor *GovernorCallerSession) GetVotesWithParams(account common.Address, timepoint *big.Int, params []byte) (*big.Int, error) { + return _Governor.Contract.GetVotesWithParams(&_Governor.CallOpts, account, timepoint, params) +} + +// HasVoted is a free data retrieval call binding the contract method 0x43859632. +// +// Solidity: function hasVoted(uint256 _proposalId, address _account) view returns(bool) +func (_Governor *GovernorCaller) HasVoted(opts *bind.CallOpts, _proposalId *big.Int, _account common.Address) (bool, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "hasVoted", _proposalId, _account) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// HasVoted is a free data retrieval call binding the contract method 0x43859632. +// +// Solidity: function hasVoted(uint256 _proposalId, address _account) view returns(bool) +func (_Governor *GovernorSession) HasVoted(_proposalId *big.Int, _account common.Address) (bool, error) { + return _Governor.Contract.HasVoted(&_Governor.CallOpts, _proposalId, _account) +} + +// HasVoted is a free data retrieval call binding the contract method 0x43859632. +// +// Solidity: function hasVoted(uint256 _proposalId, address _account) view returns(bool) +func (_Governor *GovernorCallerSession) HasVoted(_proposalId *big.Int, _account common.Address) (bool, error) { + return _Governor.Contract.HasVoted(&_Governor.CallOpts, _proposalId, _account) +} + +// HashProposal is a free data retrieval call binding the contract method 0xc59057e4. +// +// Solidity: function hashProposal(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) pure returns(uint256) +func (_Governor *GovernorCaller) HashProposal(opts *bind.CallOpts, targets []common.Address, values []*big.Int, calldatas [][]byte, descriptionHash [32]byte) (*big.Int, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "hashProposal", targets, values, calldatas, descriptionHash) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// HashProposal is a free data retrieval call binding the contract method 0xc59057e4. +// +// Solidity: function hashProposal(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) pure returns(uint256) +func (_Governor *GovernorSession) HashProposal(targets []common.Address, values []*big.Int, calldatas [][]byte, descriptionHash [32]byte) (*big.Int, error) { + return _Governor.Contract.HashProposal(&_Governor.CallOpts, targets, values, calldatas, descriptionHash) +} + +// HashProposal is a free data retrieval call binding the contract method 0xc59057e4. +// +// Solidity: function hashProposal(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) pure returns(uint256) +func (_Governor *GovernorCallerSession) HashProposal(targets []common.Address, values []*big.Int, calldatas [][]byte, descriptionHash [32]byte) (*big.Int, error) { + return _Governor.Contract.HashProposal(&_Governor.CallOpts, targets, values, calldatas, descriptionHash) +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_Governor *GovernorCaller) Name(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "name") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_Governor *GovernorSession) Name() (string, error) { + return _Governor.Contract.Name(&_Governor.CallOpts) +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_Governor *GovernorCallerSession) Name() (string, error) { + return _Governor.Contract.Name(&_Governor.CallOpts) +} + +// ProposalDeadline is a free data retrieval call binding the contract method 0xc01f9e37. +// +// Solidity: function proposalDeadline(uint256 proposalId) view returns(uint256) +func (_Governor *GovernorCaller) ProposalDeadline(opts *bind.CallOpts, proposalId *big.Int) (*big.Int, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "proposalDeadline", proposalId) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// ProposalDeadline is a free data retrieval call binding the contract method 0xc01f9e37. +// +// Solidity: function proposalDeadline(uint256 proposalId) view returns(uint256) +func (_Governor *GovernorSession) ProposalDeadline(proposalId *big.Int) (*big.Int, error) { + return _Governor.Contract.ProposalDeadline(&_Governor.CallOpts, proposalId) +} + +// ProposalDeadline is a free data retrieval call binding the contract method 0xc01f9e37. +// +// Solidity: function proposalDeadline(uint256 proposalId) view returns(uint256) +func (_Governor *GovernorCallerSession) ProposalDeadline(proposalId *big.Int) (*big.Int, error) { + return _Governor.Contract.ProposalDeadline(&_Governor.CallOpts, proposalId) +} + +// ProposalEta is a free data retrieval call binding the contract method 0xab58fb8e. +// +// Solidity: function proposalEta(uint256 proposalId) view returns(uint256) +func (_Governor *GovernorCaller) ProposalEta(opts *bind.CallOpts, proposalId *big.Int) (*big.Int, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "proposalEta", proposalId) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// ProposalEta is a free data retrieval call binding the contract method 0xab58fb8e. +// +// Solidity: function proposalEta(uint256 proposalId) view returns(uint256) +func (_Governor *GovernorSession) ProposalEta(proposalId *big.Int) (*big.Int, error) { + return _Governor.Contract.ProposalEta(&_Governor.CallOpts, proposalId) +} + +// ProposalEta is a free data retrieval call binding the contract method 0xab58fb8e. +// +// Solidity: function proposalEta(uint256 proposalId) view returns(uint256) +func (_Governor *GovernorCallerSession) ProposalEta(proposalId *big.Int) (*big.Int, error) { + return _Governor.Contract.ProposalEta(&_Governor.CallOpts, proposalId) +} + +// ProposalProposer is a free data retrieval call binding the contract method 0x143489d0. +// +// Solidity: function proposalProposer(uint256 proposalId) view returns(address) +func (_Governor *GovernorCaller) ProposalProposer(opts *bind.CallOpts, proposalId *big.Int) (common.Address, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "proposalProposer", proposalId) + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// ProposalProposer is a free data retrieval call binding the contract method 0x143489d0. +// +// Solidity: function proposalProposer(uint256 proposalId) view returns(address) +func (_Governor *GovernorSession) ProposalProposer(proposalId *big.Int) (common.Address, error) { + return _Governor.Contract.ProposalProposer(&_Governor.CallOpts, proposalId) +} + +// ProposalProposer is a free data retrieval call binding the contract method 0x143489d0. +// +// Solidity: function proposalProposer(uint256 proposalId) view returns(address) +func (_Governor *GovernorCallerSession) ProposalProposer(proposalId *big.Int) (common.Address, error) { + return _Governor.Contract.ProposalProposer(&_Governor.CallOpts, proposalId) +} + +// ProposalSnapshot is a free data retrieval call binding the contract method 0x2d63f693. +// +// Solidity: function proposalSnapshot(uint256 proposalId) view returns(uint256) +func (_Governor *GovernorCaller) ProposalSnapshot(opts *bind.CallOpts, proposalId *big.Int) (*big.Int, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "proposalSnapshot", proposalId) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// ProposalSnapshot is a free data retrieval call binding the contract method 0x2d63f693. +// +// Solidity: function proposalSnapshot(uint256 proposalId) view returns(uint256) +func (_Governor *GovernorSession) ProposalSnapshot(proposalId *big.Int) (*big.Int, error) { + return _Governor.Contract.ProposalSnapshot(&_Governor.CallOpts, proposalId) +} + +// ProposalSnapshot is a free data retrieval call binding the contract method 0x2d63f693. +// +// Solidity: function proposalSnapshot(uint256 proposalId) view returns(uint256) +func (_Governor *GovernorCallerSession) ProposalSnapshot(proposalId *big.Int) (*big.Int, error) { + return _Governor.Contract.ProposalSnapshot(&_Governor.CallOpts, proposalId) +} + +// ProposalThreshold is a free data retrieval call binding the contract method 0xb58131b0. +// +// Solidity: function proposalThreshold() view returns(uint256) +func (_Governor *GovernorCaller) ProposalThreshold(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "proposalThreshold") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// ProposalThreshold is a free data retrieval call binding the contract method 0xb58131b0. +// +// Solidity: function proposalThreshold() view returns(uint256) +func (_Governor *GovernorSession) ProposalThreshold() (*big.Int, error) { + return _Governor.Contract.ProposalThreshold(&_Governor.CallOpts) +} + +// ProposalThreshold is a free data retrieval call binding the contract method 0xb58131b0. +// +// Solidity: function proposalThreshold() view returns(uint256) +func (_Governor *GovernorCallerSession) ProposalThreshold() (*big.Int, error) { + return _Governor.Contract.ProposalThreshold(&_Governor.CallOpts) +} + +// ProposalVotes is a free data retrieval call binding the contract method 0x544ffc9c. +// +// Solidity: function proposalVotes(uint256 _proposalId) view returns(uint256 againstVotes, uint256 forVotes, uint256 abstainVotes) +func (_Governor *GovernorCaller) ProposalVotes(opts *bind.CallOpts, _proposalId *big.Int) (struct { + AgainstVotes *big.Int + ForVotes *big.Int + AbstainVotes *big.Int +}, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "proposalVotes", _proposalId) + + outstruct := new(struct { + AgainstVotes *big.Int + ForVotes *big.Int + AbstainVotes *big.Int + }) + if err != nil { + return *outstruct, err + } + + outstruct.AgainstVotes = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + outstruct.ForVotes = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int) + outstruct.AbstainVotes = *abi.ConvertType(out[2], new(*big.Int)).(**big.Int) + + return *outstruct, err + +} + +// ProposalVotes is a free data retrieval call binding the contract method 0x544ffc9c. +// +// Solidity: function proposalVotes(uint256 _proposalId) view returns(uint256 againstVotes, uint256 forVotes, uint256 abstainVotes) +func (_Governor *GovernorSession) ProposalVotes(_proposalId *big.Int) (struct { + AgainstVotes *big.Int + ForVotes *big.Int + AbstainVotes *big.Int +}, error) { + return _Governor.Contract.ProposalVotes(&_Governor.CallOpts, _proposalId) +} + +// ProposalVotes is a free data retrieval call binding the contract method 0x544ffc9c. +// +// Solidity: function proposalVotes(uint256 _proposalId) view returns(uint256 againstVotes, uint256 forVotes, uint256 abstainVotes) +func (_Governor *GovernorCallerSession) ProposalVotes(_proposalId *big.Int) (struct { + AgainstVotes *big.Int + ForVotes *big.Int + AbstainVotes *big.Int +}, error) { + return _Governor.Contract.ProposalVotes(&_Governor.CallOpts, _proposalId) +} + +// Quorum is a free data retrieval call binding the contract method 0xf8ce560a. +// +// Solidity: function quorum(uint256 timepoint) view returns(uint256) +func (_Governor *GovernorCaller) Quorum(opts *bind.CallOpts, timepoint *big.Int) (*big.Int, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "quorum", timepoint) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Quorum is a free data retrieval call binding the contract method 0xf8ce560a. +// +// Solidity: function quorum(uint256 timepoint) view returns(uint256) +func (_Governor *GovernorSession) Quorum(timepoint *big.Int) (*big.Int, error) { + return _Governor.Contract.Quorum(&_Governor.CallOpts, timepoint) +} + +// Quorum is a free data retrieval call binding the contract method 0xf8ce560a. +// +// Solidity: function quorum(uint256 timepoint) view returns(uint256) +func (_Governor *GovernorCallerSession) Quorum(timepoint *big.Int) (*big.Int, error) { + return _Governor.Contract.Quorum(&_Governor.CallOpts, timepoint) +} + +// QuorumDenominator is a free data retrieval call binding the contract method 0x97c3d334. +// +// Solidity: function quorumDenominator() view returns(uint256) +func (_Governor *GovernorCaller) QuorumDenominator(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "quorumDenominator") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// QuorumDenominator is a free data retrieval call binding the contract method 0x97c3d334. +// +// Solidity: function quorumDenominator() view returns(uint256) +func (_Governor *GovernorSession) QuorumDenominator() (*big.Int, error) { + return _Governor.Contract.QuorumDenominator(&_Governor.CallOpts) +} + +// QuorumDenominator is a free data retrieval call binding the contract method 0x97c3d334. +// +// Solidity: function quorumDenominator() view returns(uint256) +func (_Governor *GovernorCallerSession) QuorumDenominator() (*big.Int, error) { + return _Governor.Contract.QuorumDenominator(&_Governor.CallOpts) +} + +// QuorumNumerator is a free data retrieval call binding the contract method 0x60c4247f. +// +// Solidity: function quorumNumerator(uint256 timepoint) view returns(uint256) +func (_Governor *GovernorCaller) QuorumNumerator(opts *bind.CallOpts, timepoint *big.Int) (*big.Int, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "quorumNumerator", timepoint) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// QuorumNumerator is a free data retrieval call binding the contract method 0x60c4247f. +// +// Solidity: function quorumNumerator(uint256 timepoint) view returns(uint256) +func (_Governor *GovernorSession) QuorumNumerator(timepoint *big.Int) (*big.Int, error) { + return _Governor.Contract.QuorumNumerator(&_Governor.CallOpts, timepoint) +} + +// QuorumNumerator is a free data retrieval call binding the contract method 0x60c4247f. +// +// Solidity: function quorumNumerator(uint256 timepoint) view returns(uint256) +func (_Governor *GovernorCallerSession) QuorumNumerator(timepoint *big.Int) (*big.Int, error) { + return _Governor.Contract.QuorumNumerator(&_Governor.CallOpts, timepoint) +} + +// QuorumNumerator0 is a free data retrieval call binding the contract method 0xa7713a70. +// +// Solidity: function quorumNumerator() view returns(uint256) +func (_Governor *GovernorCaller) QuorumNumerator0(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "quorumNumerator0") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// QuorumNumerator0 is a free data retrieval call binding the contract method 0xa7713a70. +// +// Solidity: function quorumNumerator() view returns(uint256) +func (_Governor *GovernorSession) QuorumNumerator0() (*big.Int, error) { + return _Governor.Contract.QuorumNumerator0(&_Governor.CallOpts) +} + +// QuorumNumerator0 is a free data retrieval call binding the contract method 0xa7713a70. +// +// Solidity: function quorumNumerator() view returns(uint256) +func (_Governor *GovernorCallerSession) QuorumNumerator0() (*big.Int, error) { + return _Governor.Contract.QuorumNumerator0(&_Governor.CallOpts) +} + +// Quota is a free data retrieval call binding the contract method 0xcebe09c9. +// +// Solidity: function quota() view returns(uint256) +func (_Governor *GovernorCaller) Quota(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "quota") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Quota is a free data retrieval call binding the contract method 0xcebe09c9. +// +// Solidity: function quota() view returns(uint256) +func (_Governor *GovernorSession) Quota() (*big.Int, error) { + return _Governor.Contract.Quota(&_Governor.CallOpts) +} + +// Quota is a free data retrieval call binding the contract method 0xcebe09c9. +// +// Solidity: function quota() view returns(uint256) +func (_Governor *GovernorCallerSession) Quota() (*big.Int, error) { + return _Governor.Contract.Quota(&_Governor.CallOpts) +} + +// State is a free data retrieval call binding the contract method 0x3e4f49e6. +// +// Solidity: function state(uint256 proposalId) view returns(uint8) +func (_Governor *GovernorCaller) State(opts *bind.CallOpts, proposalId *big.Int) (uint8, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "state", proposalId) + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +// State is a free data retrieval call binding the contract method 0x3e4f49e6. +// +// Solidity: function state(uint256 proposalId) view returns(uint8) +func (_Governor *GovernorSession) State(proposalId *big.Int) (uint8, error) { + return _Governor.Contract.State(&_Governor.CallOpts, proposalId) +} + +// State is a free data retrieval call binding the contract method 0x3e4f49e6. +// +// Solidity: function state(uint256 proposalId) view returns(uint8) +func (_Governor *GovernorCallerSession) State(proposalId *big.Int) (uint8, error) { + return _Governor.Contract.State(&_Governor.CallOpts, proposalId) +} + +// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7. +// +// Solidity: function supportsInterface(bytes4 interfaceId) view returns(bool) +func (_Governor *GovernorCaller) SupportsInterface(opts *bind.CallOpts, interfaceId [4]byte) (bool, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "supportsInterface", interfaceId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7. +// +// Solidity: function supportsInterface(bytes4 interfaceId) view returns(bool) +func (_Governor *GovernorSession) SupportsInterface(interfaceId [4]byte) (bool, error) { + return _Governor.Contract.SupportsInterface(&_Governor.CallOpts, interfaceId) +} + +// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7. +// +// Solidity: function supportsInterface(bytes4 interfaceId) view returns(bool) +func (_Governor *GovernorCallerSession) SupportsInterface(interfaceId [4]byte) (bool, error) { + return _Governor.Contract.SupportsInterface(&_Governor.CallOpts, interfaceId) +} + +// TargetContractId is a free data retrieval call binding the contract method 0x51720b41. +// +// Solidity: function targetContractId() view returns(bytes32) +func (_Governor *GovernorCaller) TargetContractId(opts *bind.CallOpts) ([32]byte, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "targetContractId") + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// TargetContractId is a free data retrieval call binding the contract method 0x51720b41. +// +// Solidity: function targetContractId() view returns(bytes32) +func (_Governor *GovernorSession) TargetContractId() ([32]byte, error) { + return _Governor.Contract.TargetContractId(&_Governor.CallOpts) +} + +// TargetContractId is a free data retrieval call binding the contract method 0x51720b41. +// +// Solidity: function targetContractId() view returns(bytes32) +func (_Governor *GovernorCallerSession) TargetContractId() ([32]byte, error) { + return _Governor.Contract.TargetContractId(&_Governor.CallOpts) +} + +// Timelock is a free data retrieval call binding the contract method 0xd33219b4. +// +// Solidity: function timelock() view returns(address) +func (_Governor *GovernorCaller) Timelock(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "timelock") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Timelock is a free data retrieval call binding the contract method 0xd33219b4. +// +// Solidity: function timelock() view returns(address) +func (_Governor *GovernorSession) Timelock() (common.Address, error) { + return _Governor.Contract.Timelock(&_Governor.CallOpts) +} + +// Timelock is a free data retrieval call binding the contract method 0xd33219b4. +// +// Solidity: function timelock() view returns(address) +func (_Governor *GovernorCallerSession) Timelock() (common.Address, error) { + return _Governor.Contract.Timelock(&_Governor.CallOpts) +} + +// Token is a free data retrieval call binding the contract method 0xfc0c546a. +// +// Solidity: function token() view returns(address) +func (_Governor *GovernorCaller) Token(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "token") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Token is a free data retrieval call binding the contract method 0xfc0c546a. +// +// Solidity: function token() view returns(address) +func (_Governor *GovernorSession) Token() (common.Address, error) { + return _Governor.Contract.Token(&_Governor.CallOpts) +} + +// Token is a free data retrieval call binding the contract method 0xfc0c546a. +// +// Solidity: function token() view returns(address) +func (_Governor *GovernorCallerSession) Token() (common.Address, error) { + return _Governor.Contract.Token(&_Governor.CallOpts) +} + +// Version is a free data retrieval call binding the contract method 0x54fd4d50. +// +// Solidity: function version() view returns(string) +func (_Governor *GovernorCaller) Version(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "version") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// Version is a free data retrieval call binding the contract method 0x54fd4d50. +// +// Solidity: function version() view returns(string) +func (_Governor *GovernorSession) Version() (string, error) { + return _Governor.Contract.Version(&_Governor.CallOpts) +} + +// Version is a free data retrieval call binding the contract method 0x54fd4d50. +// +// Solidity: function version() view returns(string) +func (_Governor *GovernorCallerSession) Version() (string, error) { + return _Governor.Contract.Version(&_Governor.CallOpts) +} + +// Votes is a free data retrieval call binding the contract method 0xe168c3ec. +// +// Solidity: function votes() view returns(address) +func (_Governor *GovernorCaller) Votes(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "votes") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Votes is a free data retrieval call binding the contract method 0xe168c3ec. +// +// Solidity: function votes() view returns(address) +func (_Governor *GovernorSession) Votes() (common.Address, error) { + return _Governor.Contract.Votes(&_Governor.CallOpts) +} + +// Votes is a free data retrieval call binding the contract method 0xe168c3ec. +// +// Solidity: function votes() view returns(address) +func (_Governor *GovernorCallerSession) Votes() (common.Address, error) { + return _Governor.Contract.Votes(&_Governor.CallOpts) +} + +// VotingDelay is a free data retrieval call binding the contract method 0x3932abb1. +// +// Solidity: function votingDelay() view returns(uint256) +func (_Governor *GovernorCaller) VotingDelay(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "votingDelay") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// VotingDelay is a free data retrieval call binding the contract method 0x3932abb1. +// +// Solidity: function votingDelay() view returns(uint256) +func (_Governor *GovernorSession) VotingDelay() (*big.Int, error) { + return _Governor.Contract.VotingDelay(&_Governor.CallOpts) +} + +// VotingDelay is a free data retrieval call binding the contract method 0x3932abb1. +// +// Solidity: function votingDelay() view returns(uint256) +func (_Governor *GovernorCallerSession) VotingDelay() (*big.Int, error) { + return _Governor.Contract.VotingDelay(&_Governor.CallOpts) +} + +// VotingPeriod is a free data retrieval call binding the contract method 0x02a251a3. +// +// Solidity: function votingPeriod() view returns(uint256) +func (_Governor *GovernorCaller) VotingPeriod(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Governor.contract.Call(opts, &out, "votingPeriod") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// VotingPeriod is a free data retrieval call binding the contract method 0x02a251a3. +// +// Solidity: function votingPeriod() view returns(uint256) +func (_Governor *GovernorSession) VotingPeriod() (*big.Int, error) { + return _Governor.Contract.VotingPeriod(&_Governor.CallOpts) +} + +// VotingPeriod is a free data retrieval call binding the contract method 0x02a251a3. +// +// Solidity: function votingPeriod() view returns(uint256) +func (_Governor *GovernorCallerSession) VotingPeriod() (*big.Int, error) { + return _Governor.Contract.VotingPeriod(&_Governor.CallOpts) +} + +// BumpGovernorVotesTokenAddress is a paid mutator transaction binding the contract method 0xb70f941f. +// +// Solidity: function bumpGovernorVotesTokenAddress() returns() +func (_Governor *GovernorTransactor) BumpGovernorVotesTokenAddress(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Governor.contract.Transact(opts, "bumpGovernorVotesTokenAddress") +} + +// BumpGovernorVotesTokenAddress is a paid mutator transaction binding the contract method 0xb70f941f. +// +// Solidity: function bumpGovernorVotesTokenAddress() returns() +func (_Governor *GovernorSession) BumpGovernorVotesTokenAddress() (*types.Transaction, error) { + return _Governor.Contract.BumpGovernorVotesTokenAddress(&_Governor.TransactOpts) +} + +// BumpGovernorVotesTokenAddress is a paid mutator transaction binding the contract method 0xb70f941f. +// +// Solidity: function bumpGovernorVotesTokenAddress() returns() +func (_Governor *GovernorTransactorSession) BumpGovernorVotesTokenAddress() (*types.Transaction, error) { + return _Governor.Contract.BumpGovernorVotesTokenAddress(&_Governor.TransactOpts) +} + +// Cancel is a paid mutator transaction binding the contract method 0x452115d6. +// +// Solidity: function cancel(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) returns(uint256) +func (_Governor *GovernorTransactor) Cancel(opts *bind.TransactOpts, targets []common.Address, values []*big.Int, calldatas [][]byte, descriptionHash [32]byte) (*types.Transaction, error) { + return _Governor.contract.Transact(opts, "cancel", targets, values, calldatas, descriptionHash) +} + +// Cancel is a paid mutator transaction binding the contract method 0x452115d6. +// +// Solidity: function cancel(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) returns(uint256) +func (_Governor *GovernorSession) Cancel(targets []common.Address, values []*big.Int, calldatas [][]byte, descriptionHash [32]byte) (*types.Transaction, error) { + return _Governor.Contract.Cancel(&_Governor.TransactOpts, targets, values, calldatas, descriptionHash) +} + +// Cancel is a paid mutator transaction binding the contract method 0x452115d6. +// +// Solidity: function cancel(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) returns(uint256) +func (_Governor *GovernorTransactorSession) Cancel(targets []common.Address, values []*big.Int, calldatas [][]byte, descriptionHash [32]byte) (*types.Transaction, error) { + return _Governor.Contract.Cancel(&_Governor.TransactOpts, targets, values, calldatas, descriptionHash) +} + +// CastVote is a paid mutator transaction binding the contract method 0x56781388. +// +// Solidity: function castVote(uint256 proposalId, uint8 support) returns(uint256) +func (_Governor *GovernorTransactor) CastVote(opts *bind.TransactOpts, proposalId *big.Int, support uint8) (*types.Transaction, error) { + return _Governor.contract.Transact(opts, "castVote", proposalId, support) +} + +// CastVote is a paid mutator transaction binding the contract method 0x56781388. +// +// Solidity: function castVote(uint256 proposalId, uint8 support) returns(uint256) +func (_Governor *GovernorSession) CastVote(proposalId *big.Int, support uint8) (*types.Transaction, error) { + return _Governor.Contract.CastVote(&_Governor.TransactOpts, proposalId, support) +} + +// CastVote is a paid mutator transaction binding the contract method 0x56781388. +// +// Solidity: function castVote(uint256 proposalId, uint8 support) returns(uint256) +func (_Governor *GovernorTransactorSession) CastVote(proposalId *big.Int, support uint8) (*types.Transaction, error) { + return _Governor.Contract.CastVote(&_Governor.TransactOpts, proposalId, support) +} + +// CastVoteBySig is a paid mutator transaction binding the contract method 0x3bccf4fd. +// +// Solidity: function castVoteBySig(uint256 proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) returns(uint256) +func (_Governor *GovernorTransactor) CastVoteBySig(opts *bind.TransactOpts, proposalId *big.Int, support uint8, v uint8, r [32]byte, s [32]byte) (*types.Transaction, error) { + return _Governor.contract.Transact(opts, "castVoteBySig", proposalId, support, v, r, s) +} + +// CastVoteBySig is a paid mutator transaction binding the contract method 0x3bccf4fd. +// +// Solidity: function castVoteBySig(uint256 proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) returns(uint256) +func (_Governor *GovernorSession) CastVoteBySig(proposalId *big.Int, support uint8, v uint8, r [32]byte, s [32]byte) (*types.Transaction, error) { + return _Governor.Contract.CastVoteBySig(&_Governor.TransactOpts, proposalId, support, v, r, s) +} + +// CastVoteBySig is a paid mutator transaction binding the contract method 0x3bccf4fd. +// +// Solidity: function castVoteBySig(uint256 proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) returns(uint256) +func (_Governor *GovernorTransactorSession) CastVoteBySig(proposalId *big.Int, support uint8, v uint8, r [32]byte, s [32]byte) (*types.Transaction, error) { + return _Governor.Contract.CastVoteBySig(&_Governor.TransactOpts, proposalId, support, v, r, s) +} + +// CastVoteWithReason is a paid mutator transaction binding the contract method 0x7b3c71d3. +// +// Solidity: function castVoteWithReason(uint256 proposalId, uint8 support, string reason) returns(uint256) +func (_Governor *GovernorTransactor) CastVoteWithReason(opts *bind.TransactOpts, proposalId *big.Int, support uint8, reason string) (*types.Transaction, error) { + return _Governor.contract.Transact(opts, "castVoteWithReason", proposalId, support, reason) +} + +// CastVoteWithReason is a paid mutator transaction binding the contract method 0x7b3c71d3. +// +// Solidity: function castVoteWithReason(uint256 proposalId, uint8 support, string reason) returns(uint256) +func (_Governor *GovernorSession) CastVoteWithReason(proposalId *big.Int, support uint8, reason string) (*types.Transaction, error) { + return _Governor.Contract.CastVoteWithReason(&_Governor.TransactOpts, proposalId, support, reason) +} + +// CastVoteWithReason is a paid mutator transaction binding the contract method 0x7b3c71d3. +// +// Solidity: function castVoteWithReason(uint256 proposalId, uint8 support, string reason) returns(uint256) +func (_Governor *GovernorTransactorSession) CastVoteWithReason(proposalId *big.Int, support uint8, reason string) (*types.Transaction, error) { + return _Governor.Contract.CastVoteWithReason(&_Governor.TransactOpts, proposalId, support, reason) +} + +// CastVoteWithReasonAndParams is a paid mutator transaction binding the contract method 0x5f398a14. +// +// Solidity: function castVoteWithReasonAndParams(uint256 proposalId, uint8 support, string reason, bytes params) returns(uint256) +func (_Governor *GovernorTransactor) CastVoteWithReasonAndParams(opts *bind.TransactOpts, proposalId *big.Int, support uint8, reason string, params []byte) (*types.Transaction, error) { + return _Governor.contract.Transact(opts, "castVoteWithReasonAndParams", proposalId, support, reason, params) +} + +// CastVoteWithReasonAndParams is a paid mutator transaction binding the contract method 0x5f398a14. +// +// Solidity: function castVoteWithReasonAndParams(uint256 proposalId, uint8 support, string reason, bytes params) returns(uint256) +func (_Governor *GovernorSession) CastVoteWithReasonAndParams(proposalId *big.Int, support uint8, reason string, params []byte) (*types.Transaction, error) { + return _Governor.Contract.CastVoteWithReasonAndParams(&_Governor.TransactOpts, proposalId, support, reason, params) +} + +// CastVoteWithReasonAndParams is a paid mutator transaction binding the contract method 0x5f398a14. +// +// Solidity: function castVoteWithReasonAndParams(uint256 proposalId, uint8 support, string reason, bytes params) returns(uint256) +func (_Governor *GovernorTransactorSession) CastVoteWithReasonAndParams(proposalId *big.Int, support uint8, reason string, params []byte) (*types.Transaction, error) { + return _Governor.Contract.CastVoteWithReasonAndParams(&_Governor.TransactOpts, proposalId, support, reason, params) +} + +// CastVoteWithReasonAndParamsBySig is a paid mutator transaction binding the contract method 0x03420181. +// +// Solidity: function castVoteWithReasonAndParamsBySig(uint256 proposalId, uint8 support, string reason, bytes params, uint8 v, bytes32 r, bytes32 s) returns(uint256) +func (_Governor *GovernorTransactor) CastVoteWithReasonAndParamsBySig(opts *bind.TransactOpts, proposalId *big.Int, support uint8, reason string, params []byte, v uint8, r [32]byte, s [32]byte) (*types.Transaction, error) { + return _Governor.contract.Transact(opts, "castVoteWithReasonAndParamsBySig", proposalId, support, reason, params, v, r, s) +} + +// CastVoteWithReasonAndParamsBySig is a paid mutator transaction binding the contract method 0x03420181. +// +// Solidity: function castVoteWithReasonAndParamsBySig(uint256 proposalId, uint8 support, string reason, bytes params, uint8 v, bytes32 r, bytes32 s) returns(uint256) +func (_Governor *GovernorSession) CastVoteWithReasonAndParamsBySig(proposalId *big.Int, support uint8, reason string, params []byte, v uint8, r [32]byte, s [32]byte) (*types.Transaction, error) { + return _Governor.Contract.CastVoteWithReasonAndParamsBySig(&_Governor.TransactOpts, proposalId, support, reason, params, v, r, s) +} + +// CastVoteWithReasonAndParamsBySig is a paid mutator transaction binding the contract method 0x03420181. +// +// Solidity: function castVoteWithReasonAndParamsBySig(uint256 proposalId, uint8 support, string reason, bytes params, uint8 v, bytes32 r, bytes32 s) returns(uint256) +func (_Governor *GovernorTransactorSession) CastVoteWithReasonAndParamsBySig(proposalId *big.Int, support uint8, reason string, params []byte, v uint8, r [32]byte, s [32]byte) (*types.Transaction, error) { + return _Governor.Contract.CastVoteWithReasonAndParamsBySig(&_Governor.TransactOpts, proposalId, support, reason, params, v, r, s) +} + +// Execute is a paid mutator transaction binding the contract method 0x2656227d. +// +// Solidity: function execute(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) payable returns(uint256) +func (_Governor *GovernorTransactor) Execute(opts *bind.TransactOpts, targets []common.Address, values []*big.Int, calldatas [][]byte, descriptionHash [32]byte) (*types.Transaction, error) { + return _Governor.contract.Transact(opts, "execute", targets, values, calldatas, descriptionHash) +} + +// Execute is a paid mutator transaction binding the contract method 0x2656227d. +// +// Solidity: function execute(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) payable returns(uint256) +func (_Governor *GovernorSession) Execute(targets []common.Address, values []*big.Int, calldatas [][]byte, descriptionHash [32]byte) (*types.Transaction, error) { + return _Governor.Contract.Execute(&_Governor.TransactOpts, targets, values, calldatas, descriptionHash) +} + +// Execute is a paid mutator transaction binding the contract method 0x2656227d. +// +// Solidity: function execute(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) payable returns(uint256) +func (_Governor *GovernorTransactorSession) Execute(targets []common.Address, values []*big.Int, calldatas [][]byte, descriptionHash [32]byte) (*types.Transaction, error) { + return _Governor.Contract.Execute(&_Governor.TransactOpts, targets, values, calldatas, descriptionHash) +} + +// Initialize is a paid mutator transaction binding the contract method 0xdcbab608. +// +// Solidity: function initialize(uint256 initialVotingDelay, uint256 initialVotingPeriod, uint256 initialProposalThreshold, uint256 initialQuorum, uint256 quota) returns() +func (_Governor *GovernorTransactor) Initialize(opts *bind.TransactOpts, initialVotingDelay *big.Int, initialVotingPeriod *big.Int, initialProposalThreshold *big.Int, initialQuorum *big.Int, quota *big.Int) (*types.Transaction, error) { + return _Governor.contract.Transact(opts, "initialize", initialVotingDelay, initialVotingPeriod, initialProposalThreshold, initialQuorum, quota) +} + +// Initialize is a paid mutator transaction binding the contract method 0xdcbab608. +// +// Solidity: function initialize(uint256 initialVotingDelay, uint256 initialVotingPeriod, uint256 initialProposalThreshold, uint256 initialQuorum, uint256 quota) returns() +func (_Governor *GovernorSession) Initialize(initialVotingDelay *big.Int, initialVotingPeriod *big.Int, initialProposalThreshold *big.Int, initialQuorum *big.Int, quota *big.Int) (*types.Transaction, error) { + return _Governor.Contract.Initialize(&_Governor.TransactOpts, initialVotingDelay, initialVotingPeriod, initialProposalThreshold, initialQuorum, quota) +} + +// Initialize is a paid mutator transaction binding the contract method 0xdcbab608. +// +// Solidity: function initialize(uint256 initialVotingDelay, uint256 initialVotingPeriod, uint256 initialProposalThreshold, uint256 initialQuorum, uint256 quota) returns() +func (_Governor *GovernorTransactorSession) Initialize(initialVotingDelay *big.Int, initialVotingPeriod *big.Int, initialProposalThreshold *big.Int, initialQuorum *big.Int, quota *big.Int) (*types.Transaction, error) { + return _Governor.Contract.Initialize(&_Governor.TransactOpts, initialVotingDelay, initialVotingPeriod, initialProposalThreshold, initialQuorum, quota) +} + +// OnERC1155BatchReceived is a paid mutator transaction binding the contract method 0xbc197c81. +// +// Solidity: function onERC1155BatchReceived(address , address , uint256[] , uint256[] , bytes ) returns(bytes4) +func (_Governor *GovernorTransactor) OnERC1155BatchReceived(opts *bind.TransactOpts, arg0 common.Address, arg1 common.Address, arg2 []*big.Int, arg3 []*big.Int, arg4 []byte) (*types.Transaction, error) { + return _Governor.contract.Transact(opts, "onERC1155BatchReceived", arg0, arg1, arg2, arg3, arg4) +} + +// OnERC1155BatchReceived is a paid mutator transaction binding the contract method 0xbc197c81. +// +// Solidity: function onERC1155BatchReceived(address , address , uint256[] , uint256[] , bytes ) returns(bytes4) +func (_Governor *GovernorSession) OnERC1155BatchReceived(arg0 common.Address, arg1 common.Address, arg2 []*big.Int, arg3 []*big.Int, arg4 []byte) (*types.Transaction, error) { + return _Governor.Contract.OnERC1155BatchReceived(&_Governor.TransactOpts, arg0, arg1, arg2, arg3, arg4) +} + +// OnERC1155BatchReceived is a paid mutator transaction binding the contract method 0xbc197c81. +// +// Solidity: function onERC1155BatchReceived(address , address , uint256[] , uint256[] , bytes ) returns(bytes4) +func (_Governor *GovernorTransactorSession) OnERC1155BatchReceived(arg0 common.Address, arg1 common.Address, arg2 []*big.Int, arg3 []*big.Int, arg4 []byte) (*types.Transaction, error) { + return _Governor.Contract.OnERC1155BatchReceived(&_Governor.TransactOpts, arg0, arg1, arg2, arg3, arg4) +} + +// OnERC1155Received is a paid mutator transaction binding the contract method 0xf23a6e61. +// +// Solidity: function onERC1155Received(address , address , uint256 , uint256 , bytes ) returns(bytes4) +func (_Governor *GovernorTransactor) OnERC1155Received(opts *bind.TransactOpts, arg0 common.Address, arg1 common.Address, arg2 *big.Int, arg3 *big.Int, arg4 []byte) (*types.Transaction, error) { + return _Governor.contract.Transact(opts, "onERC1155Received", arg0, arg1, arg2, arg3, arg4) +} + +// OnERC1155Received is a paid mutator transaction binding the contract method 0xf23a6e61. +// +// Solidity: function onERC1155Received(address , address , uint256 , uint256 , bytes ) returns(bytes4) +func (_Governor *GovernorSession) OnERC1155Received(arg0 common.Address, arg1 common.Address, arg2 *big.Int, arg3 *big.Int, arg4 []byte) (*types.Transaction, error) { + return _Governor.Contract.OnERC1155Received(&_Governor.TransactOpts, arg0, arg1, arg2, arg3, arg4) +} + +// OnERC1155Received is a paid mutator transaction binding the contract method 0xf23a6e61. +// +// Solidity: function onERC1155Received(address , address , uint256 , uint256 , bytes ) returns(bytes4) +func (_Governor *GovernorTransactorSession) OnERC1155Received(arg0 common.Address, arg1 common.Address, arg2 *big.Int, arg3 *big.Int, arg4 []byte) (*types.Transaction, error) { + return _Governor.Contract.OnERC1155Received(&_Governor.TransactOpts, arg0, arg1, arg2, arg3, arg4) +} + +// OnERC721Received is a paid mutator transaction binding the contract method 0x150b7a02. +// +// Solidity: function onERC721Received(address , address , uint256 , bytes ) returns(bytes4) +func (_Governor *GovernorTransactor) OnERC721Received(opts *bind.TransactOpts, arg0 common.Address, arg1 common.Address, arg2 *big.Int, arg3 []byte) (*types.Transaction, error) { + return _Governor.contract.Transact(opts, "onERC721Received", arg0, arg1, arg2, arg3) +} + +// OnERC721Received is a paid mutator transaction binding the contract method 0x150b7a02. +// +// Solidity: function onERC721Received(address , address , uint256 , bytes ) returns(bytes4) +func (_Governor *GovernorSession) OnERC721Received(arg0 common.Address, arg1 common.Address, arg2 *big.Int, arg3 []byte) (*types.Transaction, error) { + return _Governor.Contract.OnERC721Received(&_Governor.TransactOpts, arg0, arg1, arg2, arg3) +} + +// OnERC721Received is a paid mutator transaction binding the contract method 0x150b7a02. +// +// Solidity: function onERC721Received(address , address , uint256 , bytes ) returns(bytes4) +func (_Governor *GovernorTransactorSession) OnERC721Received(arg0 common.Address, arg1 common.Address, arg2 *big.Int, arg3 []byte) (*types.Transaction, error) { + return _Governor.Contract.OnERC721Received(&_Governor.TransactOpts, arg0, arg1, arg2, arg3) +} + +// Propose is a paid mutator transaction binding the contract method 0x7d5e81e2. +// +// Solidity: function propose(address[] targets, uint256[] values, bytes[] calldatas, string description) returns(uint256) +func (_Governor *GovernorTransactor) Propose(opts *bind.TransactOpts, targets []common.Address, values []*big.Int, calldatas [][]byte, description string) (*types.Transaction, error) { + return _Governor.contract.Transact(opts, "propose", targets, values, calldatas, description) +} + +// Propose is a paid mutator transaction binding the contract method 0x7d5e81e2. +// +// Solidity: function propose(address[] targets, uint256[] values, bytes[] calldatas, string description) returns(uint256) +func (_Governor *GovernorSession) Propose(targets []common.Address, values []*big.Int, calldatas [][]byte, description string) (*types.Transaction, error) { + return _Governor.Contract.Propose(&_Governor.TransactOpts, targets, values, calldatas, description) +} + +// Propose is a paid mutator transaction binding the contract method 0x7d5e81e2. +// +// Solidity: function propose(address[] targets, uint256[] values, bytes[] calldatas, string description) returns(uint256) +func (_Governor *GovernorTransactorSession) Propose(targets []common.Address, values []*big.Int, calldatas [][]byte, description string) (*types.Transaction, error) { + return _Governor.Contract.Propose(&_Governor.TransactOpts, targets, values, calldatas, description) +} + +// Queue is a paid mutator transaction binding the contract method 0x160cbed7. +// +// Solidity: function queue(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) returns(uint256) +func (_Governor *GovernorTransactor) Queue(opts *bind.TransactOpts, targets []common.Address, values []*big.Int, calldatas [][]byte, descriptionHash [32]byte) (*types.Transaction, error) { + return _Governor.contract.Transact(opts, "queue", targets, values, calldatas, descriptionHash) +} + +// Queue is a paid mutator transaction binding the contract method 0x160cbed7. +// +// Solidity: function queue(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) returns(uint256) +func (_Governor *GovernorSession) Queue(targets []common.Address, values []*big.Int, calldatas [][]byte, descriptionHash [32]byte) (*types.Transaction, error) { + return _Governor.Contract.Queue(&_Governor.TransactOpts, targets, values, calldatas, descriptionHash) +} + +// Queue is a paid mutator transaction binding the contract method 0x160cbed7. +// +// Solidity: function queue(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) returns(uint256) +func (_Governor *GovernorTransactorSession) Queue(targets []common.Address, values []*big.Int, calldatas [][]byte, descriptionHash [32]byte) (*types.Transaction, error) { + return _Governor.Contract.Queue(&_Governor.TransactOpts, targets, values, calldatas, descriptionHash) +} + +// Relay is a paid mutator transaction binding the contract method 0xc28bc2fa. +// +// Solidity: function relay(address target, uint256 value, bytes data) payable returns() +func (_Governor *GovernorTransactor) Relay(opts *bind.TransactOpts, target common.Address, value *big.Int, data []byte) (*types.Transaction, error) { + return _Governor.contract.Transact(opts, "relay", target, value, data) +} + +// Relay is a paid mutator transaction binding the contract method 0xc28bc2fa. +// +// Solidity: function relay(address target, uint256 value, bytes data) payable returns() +func (_Governor *GovernorSession) Relay(target common.Address, value *big.Int, data []byte) (*types.Transaction, error) { + return _Governor.Contract.Relay(&_Governor.TransactOpts, target, value, data) +} + +// Relay is a paid mutator transaction binding the contract method 0xc28bc2fa. +// +// Solidity: function relay(address target, uint256 value, bytes data) payable returns() +func (_Governor *GovernorTransactorSession) Relay(target common.Address, value *big.Int, data []byte) (*types.Transaction, error) { + return _Governor.Contract.Relay(&_Governor.TransactOpts, target, value, data) +} + +// SetController is a paid mutator transaction binding the contract method 0x92eefe9b. +// +// Solidity: function setController(address _controller) returns() +func (_Governor *GovernorTransactor) SetController(opts *bind.TransactOpts, _controller common.Address) (*types.Transaction, error) { + return _Governor.contract.Transact(opts, "setController", _controller) +} + +// SetController is a paid mutator transaction binding the contract method 0x92eefe9b. +// +// Solidity: function setController(address _controller) returns() +func (_Governor *GovernorSession) SetController(_controller common.Address) (*types.Transaction, error) { + return _Governor.Contract.SetController(&_Governor.TransactOpts, _controller) +} + +// SetController is a paid mutator transaction binding the contract method 0x92eefe9b. +// +// Solidity: function setController(address _controller) returns() +func (_Governor *GovernorTransactorSession) SetController(_controller common.Address) (*types.Transaction, error) { + return _Governor.Contract.SetController(&_Governor.TransactOpts, _controller) +} + +// SetProposalThreshold is a paid mutator transaction binding the contract method 0xece40cc1. +// +// Solidity: function setProposalThreshold(uint256 newProposalThreshold) returns() +func (_Governor *GovernorTransactor) SetProposalThreshold(opts *bind.TransactOpts, newProposalThreshold *big.Int) (*types.Transaction, error) { + return _Governor.contract.Transact(opts, "setProposalThreshold", newProposalThreshold) +} + +// SetProposalThreshold is a paid mutator transaction binding the contract method 0xece40cc1. +// +// Solidity: function setProposalThreshold(uint256 newProposalThreshold) returns() +func (_Governor *GovernorSession) SetProposalThreshold(newProposalThreshold *big.Int) (*types.Transaction, error) { + return _Governor.Contract.SetProposalThreshold(&_Governor.TransactOpts, newProposalThreshold) +} + +// SetProposalThreshold is a paid mutator transaction binding the contract method 0xece40cc1. +// +// Solidity: function setProposalThreshold(uint256 newProposalThreshold) returns() +func (_Governor *GovernorTransactorSession) SetProposalThreshold(newProposalThreshold *big.Int) (*types.Transaction, error) { + return _Governor.Contract.SetProposalThreshold(&_Governor.TransactOpts, newProposalThreshold) +} + +// SetVotingDelay is a paid mutator transaction binding the contract method 0x70b0f660. +// +// Solidity: function setVotingDelay(uint256 newVotingDelay) returns() +func (_Governor *GovernorTransactor) SetVotingDelay(opts *bind.TransactOpts, newVotingDelay *big.Int) (*types.Transaction, error) { + return _Governor.contract.Transact(opts, "setVotingDelay", newVotingDelay) +} + +// SetVotingDelay is a paid mutator transaction binding the contract method 0x70b0f660. +// +// Solidity: function setVotingDelay(uint256 newVotingDelay) returns() +func (_Governor *GovernorSession) SetVotingDelay(newVotingDelay *big.Int) (*types.Transaction, error) { + return _Governor.Contract.SetVotingDelay(&_Governor.TransactOpts, newVotingDelay) +} + +// SetVotingDelay is a paid mutator transaction binding the contract method 0x70b0f660. +// +// Solidity: function setVotingDelay(uint256 newVotingDelay) returns() +func (_Governor *GovernorTransactorSession) SetVotingDelay(newVotingDelay *big.Int) (*types.Transaction, error) { + return _Governor.Contract.SetVotingDelay(&_Governor.TransactOpts, newVotingDelay) +} + +// SetVotingPeriod is a paid mutator transaction binding the contract method 0xea0217cf. +// +// Solidity: function setVotingPeriod(uint256 newVotingPeriod) returns() +func (_Governor *GovernorTransactor) SetVotingPeriod(opts *bind.TransactOpts, newVotingPeriod *big.Int) (*types.Transaction, error) { + return _Governor.contract.Transact(opts, "setVotingPeriod", newVotingPeriod) +} + +// SetVotingPeriod is a paid mutator transaction binding the contract method 0xea0217cf. +// +// Solidity: function setVotingPeriod(uint256 newVotingPeriod) returns() +func (_Governor *GovernorSession) SetVotingPeriod(newVotingPeriod *big.Int) (*types.Transaction, error) { + return _Governor.Contract.SetVotingPeriod(&_Governor.TransactOpts, newVotingPeriod) +} + +// SetVotingPeriod is a paid mutator transaction binding the contract method 0xea0217cf. +// +// Solidity: function setVotingPeriod(uint256 newVotingPeriod) returns() +func (_Governor *GovernorTransactorSession) SetVotingPeriod(newVotingPeriod *big.Int) (*types.Transaction, error) { + return _Governor.Contract.SetVotingPeriod(&_Governor.TransactOpts, newVotingPeriod) +} + +// UpdateQuorumNumerator is a paid mutator transaction binding the contract method 0x06f3f9e6. +// +// Solidity: function updateQuorumNumerator(uint256 newQuorumNumerator) returns() +func (_Governor *GovernorTransactor) UpdateQuorumNumerator(opts *bind.TransactOpts, newQuorumNumerator *big.Int) (*types.Transaction, error) { + return _Governor.contract.Transact(opts, "updateQuorumNumerator", newQuorumNumerator) +} + +// UpdateQuorumNumerator is a paid mutator transaction binding the contract method 0x06f3f9e6. +// +// Solidity: function updateQuorumNumerator(uint256 newQuorumNumerator) returns() +func (_Governor *GovernorSession) UpdateQuorumNumerator(newQuorumNumerator *big.Int) (*types.Transaction, error) { + return _Governor.Contract.UpdateQuorumNumerator(&_Governor.TransactOpts, newQuorumNumerator) +} + +// UpdateQuorumNumerator is a paid mutator transaction binding the contract method 0x06f3f9e6. +// +// Solidity: function updateQuorumNumerator(uint256 newQuorumNumerator) returns() +func (_Governor *GovernorTransactorSession) UpdateQuorumNumerator(newQuorumNumerator *big.Int) (*types.Transaction, error) { + return _Governor.Contract.UpdateQuorumNumerator(&_Governor.TransactOpts, newQuorumNumerator) +} + +// UpdateTimelock is a paid mutator transaction binding the contract method 0xa890c910. +// +// Solidity: function updateTimelock(address newTimelock) returns() +func (_Governor *GovernorTransactor) UpdateTimelock(opts *bind.TransactOpts, newTimelock common.Address) (*types.Transaction, error) { + return _Governor.contract.Transact(opts, "updateTimelock", newTimelock) +} + +// UpdateTimelock is a paid mutator transaction binding the contract method 0xa890c910. +// +// Solidity: function updateTimelock(address newTimelock) returns() +func (_Governor *GovernorSession) UpdateTimelock(newTimelock common.Address) (*types.Transaction, error) { + return _Governor.Contract.UpdateTimelock(&_Governor.TransactOpts, newTimelock) +} + +// UpdateTimelock is a paid mutator transaction binding the contract method 0xa890c910. +// +// Solidity: function updateTimelock(address newTimelock) returns() +func (_Governor *GovernorTransactorSession) UpdateTimelock(newTimelock common.Address) (*types.Transaction, error) { + return _Governor.Contract.UpdateTimelock(&_Governor.TransactOpts, newTimelock) +} + +// Receive is a paid mutator transaction binding the contract receive function. +// +// Solidity: receive() payable returns() +func (_Governor *GovernorTransactor) Receive(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Governor.contract.RawTransact(opts, nil) // calldata is disallowed for receive function +} + +// Receive is a paid mutator transaction binding the contract receive function. +// +// Solidity: receive() payable returns() +func (_Governor *GovernorSession) Receive() (*types.Transaction, error) { + return _Governor.Contract.Receive(&_Governor.TransactOpts) +} + +// Receive is a paid mutator transaction binding the contract receive function. +// +// Solidity: receive() payable returns() +func (_Governor *GovernorTransactorSession) Receive() (*types.Transaction, error) { + return _Governor.Contract.Receive(&_Governor.TransactOpts) +} + +// GovernorEIP712DomainChangedIterator is returned from FilterEIP712DomainChanged and is used to iterate over the raw logs and unpacked data for EIP712DomainChanged events raised by the Governor contract. +type GovernorEIP712DomainChangedIterator struct { + Event *GovernorEIP712DomainChanged // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *GovernorEIP712DomainChangedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(GovernorEIP712DomainChanged) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(GovernorEIP712DomainChanged) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *GovernorEIP712DomainChangedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *GovernorEIP712DomainChangedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// GovernorEIP712DomainChanged represents a EIP712DomainChanged event raised by the Governor contract. +type GovernorEIP712DomainChanged struct { + Raw types.Log // Blockchain specific contextual infos +} + +// FilterEIP712DomainChanged is a free log retrieval operation binding the contract event 0x0a6387c9ea3628b88a633bb4f3b151770f70085117a15f9bf3787cda53f13d31. +// +// Solidity: event EIP712DomainChanged() +func (_Governor *GovernorFilterer) FilterEIP712DomainChanged(opts *bind.FilterOpts) (*GovernorEIP712DomainChangedIterator, error) { + + logs, sub, err := _Governor.contract.FilterLogs(opts, "EIP712DomainChanged") + if err != nil { + return nil, err + } + return &GovernorEIP712DomainChangedIterator{contract: _Governor.contract, event: "EIP712DomainChanged", logs: logs, sub: sub}, nil +} + +// WatchEIP712DomainChanged is a free log subscription operation binding the contract event 0x0a6387c9ea3628b88a633bb4f3b151770f70085117a15f9bf3787cda53f13d31. +// +// Solidity: event EIP712DomainChanged() +func (_Governor *GovernorFilterer) WatchEIP712DomainChanged(opts *bind.WatchOpts, sink chan<- *GovernorEIP712DomainChanged) (event.Subscription, error) { + + logs, sub, err := _Governor.contract.WatchLogs(opts, "EIP712DomainChanged") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(GovernorEIP712DomainChanged) + if err := _Governor.contract.UnpackLog(event, "EIP712DomainChanged", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseEIP712DomainChanged is a log parse operation binding the contract event 0x0a6387c9ea3628b88a633bb4f3b151770f70085117a15f9bf3787cda53f13d31. +// +// Solidity: event EIP712DomainChanged() +func (_Governor *GovernorFilterer) ParseEIP712DomainChanged(log types.Log) (*GovernorEIP712DomainChanged, error) { + event := new(GovernorEIP712DomainChanged) + if err := _Governor.contract.UnpackLog(event, "EIP712DomainChanged", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// GovernorInitializedIterator is returned from FilterInitialized and is used to iterate over the raw logs and unpacked data for Initialized events raised by the Governor contract. +type GovernorInitializedIterator struct { + Event *GovernorInitialized // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *GovernorInitializedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(GovernorInitialized) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(GovernorInitialized) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *GovernorInitializedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *GovernorInitializedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// GovernorInitialized represents a Initialized event raised by the Governor contract. +type GovernorInitialized struct { + Version uint8 + Raw types.Log // Blockchain specific contextual infos +} + +// FilterInitialized is a free log retrieval operation binding the contract event 0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498. +// +// Solidity: event Initialized(uint8 version) +func (_Governor *GovernorFilterer) FilterInitialized(opts *bind.FilterOpts) (*GovernorInitializedIterator, error) { + + logs, sub, err := _Governor.contract.FilterLogs(opts, "Initialized") + if err != nil { + return nil, err + } + return &GovernorInitializedIterator{contract: _Governor.contract, event: "Initialized", logs: logs, sub: sub}, nil +} + +// WatchInitialized is a free log subscription operation binding the contract event 0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498. +// +// Solidity: event Initialized(uint8 version) +func (_Governor *GovernorFilterer) WatchInitialized(opts *bind.WatchOpts, sink chan<- *GovernorInitialized) (event.Subscription, error) { + + logs, sub, err := _Governor.contract.WatchLogs(opts, "Initialized") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(GovernorInitialized) + if err := _Governor.contract.UnpackLog(event, "Initialized", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseInitialized is a log parse operation binding the contract event 0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498. +// +// Solidity: event Initialized(uint8 version) +func (_Governor *GovernorFilterer) ParseInitialized(log types.Log) (*GovernorInitialized, error) { + event := new(GovernorInitialized) + if err := _Governor.contract.UnpackLog(event, "Initialized", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// GovernorParameterUpdateIterator is returned from FilterParameterUpdate and is used to iterate over the raw logs and unpacked data for ParameterUpdate events raised by the Governor contract. +type GovernorParameterUpdateIterator struct { + Event *GovernorParameterUpdate // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *GovernorParameterUpdateIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(GovernorParameterUpdate) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(GovernorParameterUpdate) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *GovernorParameterUpdateIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *GovernorParameterUpdateIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// GovernorParameterUpdate represents a ParameterUpdate event raised by the Governor contract. +type GovernorParameterUpdate struct { + Param string + Raw types.Log // Blockchain specific contextual infos +} + +// FilterParameterUpdate is a free log retrieval operation binding the contract event 0x9f5033568d78ae30f29f01e944f97b2216493bd19d1b46d429673acff3dcd674. +// +// Solidity: event ParameterUpdate(string param) +func (_Governor *GovernorFilterer) FilterParameterUpdate(opts *bind.FilterOpts) (*GovernorParameterUpdateIterator, error) { + + logs, sub, err := _Governor.contract.FilterLogs(opts, "ParameterUpdate") + if err != nil { + return nil, err + } + return &GovernorParameterUpdateIterator{contract: _Governor.contract, event: "ParameterUpdate", logs: logs, sub: sub}, nil +} + +// WatchParameterUpdate is a free log subscription operation binding the contract event 0x9f5033568d78ae30f29f01e944f97b2216493bd19d1b46d429673acff3dcd674. +// +// Solidity: event ParameterUpdate(string param) +func (_Governor *GovernorFilterer) WatchParameterUpdate(opts *bind.WatchOpts, sink chan<- *GovernorParameterUpdate) (event.Subscription, error) { + + logs, sub, err := _Governor.contract.WatchLogs(opts, "ParameterUpdate") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(GovernorParameterUpdate) + if err := _Governor.contract.UnpackLog(event, "ParameterUpdate", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseParameterUpdate is a log parse operation binding the contract event 0x9f5033568d78ae30f29f01e944f97b2216493bd19d1b46d429673acff3dcd674. +// +// Solidity: event ParameterUpdate(string param) +func (_Governor *GovernorFilterer) ParseParameterUpdate(log types.Log) (*GovernorParameterUpdate, error) { + event := new(GovernorParameterUpdate) + if err := _Governor.contract.UnpackLog(event, "ParameterUpdate", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// GovernorProposalCanceledIterator is returned from FilterProposalCanceled and is used to iterate over the raw logs and unpacked data for ProposalCanceled events raised by the Governor contract. +type GovernorProposalCanceledIterator struct { + Event *GovernorProposalCanceled // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *GovernorProposalCanceledIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(GovernorProposalCanceled) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(GovernorProposalCanceled) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *GovernorProposalCanceledIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *GovernorProposalCanceledIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// GovernorProposalCanceled represents a ProposalCanceled event raised by the Governor contract. +type GovernorProposalCanceled struct { + ProposalId *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterProposalCanceled is a free log retrieval operation binding the contract event 0x789cf55be980739dad1d0699b93b58e806b51c9d96619bfa8fe0a28abaa7b30c. +// +// Solidity: event ProposalCanceled(uint256 proposalId) +func (_Governor *GovernorFilterer) FilterProposalCanceled(opts *bind.FilterOpts) (*GovernorProposalCanceledIterator, error) { + + logs, sub, err := _Governor.contract.FilterLogs(opts, "ProposalCanceled") + if err != nil { + return nil, err + } + return &GovernorProposalCanceledIterator{contract: _Governor.contract, event: "ProposalCanceled", logs: logs, sub: sub}, nil +} + +// WatchProposalCanceled is a free log subscription operation binding the contract event 0x789cf55be980739dad1d0699b93b58e806b51c9d96619bfa8fe0a28abaa7b30c. +// +// Solidity: event ProposalCanceled(uint256 proposalId) +func (_Governor *GovernorFilterer) WatchProposalCanceled(opts *bind.WatchOpts, sink chan<- *GovernorProposalCanceled) (event.Subscription, error) { + + logs, sub, err := _Governor.contract.WatchLogs(opts, "ProposalCanceled") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(GovernorProposalCanceled) + if err := _Governor.contract.UnpackLog(event, "ProposalCanceled", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseProposalCanceled is a log parse operation binding the contract event 0x789cf55be980739dad1d0699b93b58e806b51c9d96619bfa8fe0a28abaa7b30c. +// +// Solidity: event ProposalCanceled(uint256 proposalId) +func (_Governor *GovernorFilterer) ParseProposalCanceled(log types.Log) (*GovernorProposalCanceled, error) { + event := new(GovernorProposalCanceled) + if err := _Governor.contract.UnpackLog(event, "ProposalCanceled", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// GovernorProposalCreatedIterator is returned from FilterProposalCreated and is used to iterate over the raw logs and unpacked data for ProposalCreated events raised by the Governor contract. +type GovernorProposalCreatedIterator struct { + Event *GovernorProposalCreated // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *GovernorProposalCreatedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(GovernorProposalCreated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(GovernorProposalCreated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *GovernorProposalCreatedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *GovernorProposalCreatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// GovernorProposalCreated represents a ProposalCreated event raised by the Governor contract. +type GovernorProposalCreated struct { + ProposalId *big.Int + Proposer common.Address + Targets []common.Address + Values []*big.Int + Signatures []string + Calldatas [][]byte + VoteStart *big.Int + VoteEnd *big.Int + Description string + Raw types.Log // Blockchain specific contextual infos +} + +// FilterProposalCreated is a free log retrieval operation binding the contract event 0x7d84a6263ae0d98d3329bd7b46bb4e8d6f98cd35a7adb45c274c8b7fd5ebd5e0. +// +// Solidity: event ProposalCreated(uint256 proposalId, address proposer, address[] targets, uint256[] values, string[] signatures, bytes[] calldatas, uint256 voteStart, uint256 voteEnd, string description) +func (_Governor *GovernorFilterer) FilterProposalCreated(opts *bind.FilterOpts) (*GovernorProposalCreatedIterator, error) { + + logs, sub, err := _Governor.contract.FilterLogs(opts, "ProposalCreated") + if err != nil { + return nil, err + } + return &GovernorProposalCreatedIterator{contract: _Governor.contract, event: "ProposalCreated", logs: logs, sub: sub}, nil +} + +// WatchProposalCreated is a free log subscription operation binding the contract event 0x7d84a6263ae0d98d3329bd7b46bb4e8d6f98cd35a7adb45c274c8b7fd5ebd5e0. +// +// Solidity: event ProposalCreated(uint256 proposalId, address proposer, address[] targets, uint256[] values, string[] signatures, bytes[] calldatas, uint256 voteStart, uint256 voteEnd, string description) +func (_Governor *GovernorFilterer) WatchProposalCreated(opts *bind.WatchOpts, sink chan<- *GovernorProposalCreated) (event.Subscription, error) { + + logs, sub, err := _Governor.contract.WatchLogs(opts, "ProposalCreated") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(GovernorProposalCreated) + if err := _Governor.contract.UnpackLog(event, "ProposalCreated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseProposalCreated is a log parse operation binding the contract event 0x7d84a6263ae0d98d3329bd7b46bb4e8d6f98cd35a7adb45c274c8b7fd5ebd5e0. +// +// Solidity: event ProposalCreated(uint256 proposalId, address proposer, address[] targets, uint256[] values, string[] signatures, bytes[] calldatas, uint256 voteStart, uint256 voteEnd, string description) +func (_Governor *GovernorFilterer) ParseProposalCreated(log types.Log) (*GovernorProposalCreated, error) { + event := new(GovernorProposalCreated) + if err := _Governor.contract.UnpackLog(event, "ProposalCreated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// GovernorProposalExecutedIterator is returned from FilterProposalExecuted and is used to iterate over the raw logs and unpacked data for ProposalExecuted events raised by the Governor contract. +type GovernorProposalExecutedIterator struct { + Event *GovernorProposalExecuted // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *GovernorProposalExecutedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(GovernorProposalExecuted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(GovernorProposalExecuted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *GovernorProposalExecutedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *GovernorProposalExecutedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// GovernorProposalExecuted represents a ProposalExecuted event raised by the Governor contract. +type GovernorProposalExecuted struct { + ProposalId *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterProposalExecuted is a free log retrieval operation binding the contract event 0x712ae1383f79ac853f8d882153778e0260ef8f03b504e2866e0593e04d2b291f. +// +// Solidity: event ProposalExecuted(uint256 proposalId) +func (_Governor *GovernorFilterer) FilterProposalExecuted(opts *bind.FilterOpts) (*GovernorProposalExecutedIterator, error) { + + logs, sub, err := _Governor.contract.FilterLogs(opts, "ProposalExecuted") + if err != nil { + return nil, err + } + return &GovernorProposalExecutedIterator{contract: _Governor.contract, event: "ProposalExecuted", logs: logs, sub: sub}, nil +} + +// WatchProposalExecuted is a free log subscription operation binding the contract event 0x712ae1383f79ac853f8d882153778e0260ef8f03b504e2866e0593e04d2b291f. +// +// Solidity: event ProposalExecuted(uint256 proposalId) +func (_Governor *GovernorFilterer) WatchProposalExecuted(opts *bind.WatchOpts, sink chan<- *GovernorProposalExecuted) (event.Subscription, error) { + + logs, sub, err := _Governor.contract.WatchLogs(opts, "ProposalExecuted") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(GovernorProposalExecuted) + if err := _Governor.contract.UnpackLog(event, "ProposalExecuted", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseProposalExecuted is a log parse operation binding the contract event 0x712ae1383f79ac853f8d882153778e0260ef8f03b504e2866e0593e04d2b291f. +// +// Solidity: event ProposalExecuted(uint256 proposalId) +func (_Governor *GovernorFilterer) ParseProposalExecuted(log types.Log) (*GovernorProposalExecuted, error) { + event := new(GovernorProposalExecuted) + if err := _Governor.contract.UnpackLog(event, "ProposalExecuted", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// GovernorProposalQueuedIterator is returned from FilterProposalQueued and is used to iterate over the raw logs and unpacked data for ProposalQueued events raised by the Governor contract. +type GovernorProposalQueuedIterator struct { + Event *GovernorProposalQueued // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *GovernorProposalQueuedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(GovernorProposalQueued) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(GovernorProposalQueued) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *GovernorProposalQueuedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *GovernorProposalQueuedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// GovernorProposalQueued represents a ProposalQueued event raised by the Governor contract. +type GovernorProposalQueued struct { + ProposalId *big.Int + Eta *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterProposalQueued is a free log retrieval operation binding the contract event 0x9a2e42fd6722813d69113e7d0079d3d940171428df7373df9c7f7617cfda2892. +// +// Solidity: event ProposalQueued(uint256 proposalId, uint256 eta) +func (_Governor *GovernorFilterer) FilterProposalQueued(opts *bind.FilterOpts) (*GovernorProposalQueuedIterator, error) { + + logs, sub, err := _Governor.contract.FilterLogs(opts, "ProposalQueued") + if err != nil { + return nil, err + } + return &GovernorProposalQueuedIterator{contract: _Governor.contract, event: "ProposalQueued", logs: logs, sub: sub}, nil +} + +// WatchProposalQueued is a free log subscription operation binding the contract event 0x9a2e42fd6722813d69113e7d0079d3d940171428df7373df9c7f7617cfda2892. +// +// Solidity: event ProposalQueued(uint256 proposalId, uint256 eta) +func (_Governor *GovernorFilterer) WatchProposalQueued(opts *bind.WatchOpts, sink chan<- *GovernorProposalQueued) (event.Subscription, error) { + + logs, sub, err := _Governor.contract.WatchLogs(opts, "ProposalQueued") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(GovernorProposalQueued) + if err := _Governor.contract.UnpackLog(event, "ProposalQueued", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseProposalQueued is a log parse operation binding the contract event 0x9a2e42fd6722813d69113e7d0079d3d940171428df7373df9c7f7617cfda2892. +// +// Solidity: event ProposalQueued(uint256 proposalId, uint256 eta) +func (_Governor *GovernorFilterer) ParseProposalQueued(log types.Log) (*GovernorProposalQueued, error) { + event := new(GovernorProposalQueued) + if err := _Governor.contract.UnpackLog(event, "ProposalQueued", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// GovernorProposalThresholdSetIterator is returned from FilterProposalThresholdSet and is used to iterate over the raw logs and unpacked data for ProposalThresholdSet events raised by the Governor contract. +type GovernorProposalThresholdSetIterator struct { + Event *GovernorProposalThresholdSet // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *GovernorProposalThresholdSetIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(GovernorProposalThresholdSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(GovernorProposalThresholdSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *GovernorProposalThresholdSetIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *GovernorProposalThresholdSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// GovernorProposalThresholdSet represents a ProposalThresholdSet event raised by the Governor contract. +type GovernorProposalThresholdSet struct { + OldProposalThreshold *big.Int + NewProposalThreshold *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterProposalThresholdSet is a free log retrieval operation binding the contract event 0xccb45da8d5717e6c4544694297c4ba5cf151d455c9bb0ed4fc7a38411bc05461. +// +// Solidity: event ProposalThresholdSet(uint256 oldProposalThreshold, uint256 newProposalThreshold) +func (_Governor *GovernorFilterer) FilterProposalThresholdSet(opts *bind.FilterOpts) (*GovernorProposalThresholdSetIterator, error) { + + logs, sub, err := _Governor.contract.FilterLogs(opts, "ProposalThresholdSet") + if err != nil { + return nil, err + } + return &GovernorProposalThresholdSetIterator{contract: _Governor.contract, event: "ProposalThresholdSet", logs: logs, sub: sub}, nil +} + +// WatchProposalThresholdSet is a free log subscription operation binding the contract event 0xccb45da8d5717e6c4544694297c4ba5cf151d455c9bb0ed4fc7a38411bc05461. +// +// Solidity: event ProposalThresholdSet(uint256 oldProposalThreshold, uint256 newProposalThreshold) +func (_Governor *GovernorFilterer) WatchProposalThresholdSet(opts *bind.WatchOpts, sink chan<- *GovernorProposalThresholdSet) (event.Subscription, error) { + + logs, sub, err := _Governor.contract.WatchLogs(opts, "ProposalThresholdSet") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(GovernorProposalThresholdSet) + if err := _Governor.contract.UnpackLog(event, "ProposalThresholdSet", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseProposalThresholdSet is a log parse operation binding the contract event 0xccb45da8d5717e6c4544694297c4ba5cf151d455c9bb0ed4fc7a38411bc05461. +// +// Solidity: event ProposalThresholdSet(uint256 oldProposalThreshold, uint256 newProposalThreshold) +func (_Governor *GovernorFilterer) ParseProposalThresholdSet(log types.Log) (*GovernorProposalThresholdSet, error) { + event := new(GovernorProposalThresholdSet) + if err := _Governor.contract.UnpackLog(event, "ProposalThresholdSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// GovernorQuorumNumeratorUpdatedIterator is returned from FilterQuorumNumeratorUpdated and is used to iterate over the raw logs and unpacked data for QuorumNumeratorUpdated events raised by the Governor contract. +type GovernorQuorumNumeratorUpdatedIterator struct { + Event *GovernorQuorumNumeratorUpdated // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *GovernorQuorumNumeratorUpdatedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(GovernorQuorumNumeratorUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(GovernorQuorumNumeratorUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *GovernorQuorumNumeratorUpdatedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *GovernorQuorumNumeratorUpdatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// GovernorQuorumNumeratorUpdated represents a QuorumNumeratorUpdated event raised by the Governor contract. +type GovernorQuorumNumeratorUpdated struct { + OldQuorumNumerator *big.Int + NewQuorumNumerator *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterQuorumNumeratorUpdated is a free log retrieval operation binding the contract event 0x0553476bf02ef2726e8ce5ced78d63e26e602e4a2257b1f559418e24b4633997. +// +// Solidity: event QuorumNumeratorUpdated(uint256 oldQuorumNumerator, uint256 newQuorumNumerator) +func (_Governor *GovernorFilterer) FilterQuorumNumeratorUpdated(opts *bind.FilterOpts) (*GovernorQuorumNumeratorUpdatedIterator, error) { + + logs, sub, err := _Governor.contract.FilterLogs(opts, "QuorumNumeratorUpdated") + if err != nil { + return nil, err + } + return &GovernorQuorumNumeratorUpdatedIterator{contract: _Governor.contract, event: "QuorumNumeratorUpdated", logs: logs, sub: sub}, nil +} + +// WatchQuorumNumeratorUpdated is a free log subscription operation binding the contract event 0x0553476bf02ef2726e8ce5ced78d63e26e602e4a2257b1f559418e24b4633997. +// +// Solidity: event QuorumNumeratorUpdated(uint256 oldQuorumNumerator, uint256 newQuorumNumerator) +func (_Governor *GovernorFilterer) WatchQuorumNumeratorUpdated(opts *bind.WatchOpts, sink chan<- *GovernorQuorumNumeratorUpdated) (event.Subscription, error) { + + logs, sub, err := _Governor.contract.WatchLogs(opts, "QuorumNumeratorUpdated") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(GovernorQuorumNumeratorUpdated) + if err := _Governor.contract.UnpackLog(event, "QuorumNumeratorUpdated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseQuorumNumeratorUpdated is a log parse operation binding the contract event 0x0553476bf02ef2726e8ce5ced78d63e26e602e4a2257b1f559418e24b4633997. +// +// Solidity: event QuorumNumeratorUpdated(uint256 oldQuorumNumerator, uint256 newQuorumNumerator) +func (_Governor *GovernorFilterer) ParseQuorumNumeratorUpdated(log types.Log) (*GovernorQuorumNumeratorUpdated, error) { + event := new(GovernorQuorumNumeratorUpdated) + if err := _Governor.contract.UnpackLog(event, "QuorumNumeratorUpdated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// GovernorSetControllerIterator is returned from FilterSetController and is used to iterate over the raw logs and unpacked data for SetController events raised by the Governor contract. +type GovernorSetControllerIterator struct { + Event *GovernorSetController // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *GovernorSetControllerIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(GovernorSetController) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(GovernorSetController) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *GovernorSetControllerIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *GovernorSetControllerIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// GovernorSetController represents a SetController event raised by the Governor contract. +type GovernorSetController struct { + Controller common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterSetController is a free log retrieval operation binding the contract event 0x4ff638452bbf33c012645d18ae6f05515ff5f2d1dfb0cece8cbf018c60903f70. +// +// Solidity: event SetController(address controller) +func (_Governor *GovernorFilterer) FilterSetController(opts *bind.FilterOpts) (*GovernorSetControllerIterator, error) { + + logs, sub, err := _Governor.contract.FilterLogs(opts, "SetController") + if err != nil { + return nil, err + } + return &GovernorSetControllerIterator{contract: _Governor.contract, event: "SetController", logs: logs, sub: sub}, nil +} + +// WatchSetController is a free log subscription operation binding the contract event 0x4ff638452bbf33c012645d18ae6f05515ff5f2d1dfb0cece8cbf018c60903f70. +// +// Solidity: event SetController(address controller) +func (_Governor *GovernorFilterer) WatchSetController(opts *bind.WatchOpts, sink chan<- *GovernorSetController) (event.Subscription, error) { + + logs, sub, err := _Governor.contract.WatchLogs(opts, "SetController") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(GovernorSetController) + if err := _Governor.contract.UnpackLog(event, "SetController", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseSetController is a log parse operation binding the contract event 0x4ff638452bbf33c012645d18ae6f05515ff5f2d1dfb0cece8cbf018c60903f70. +// +// Solidity: event SetController(address controller) +func (_Governor *GovernorFilterer) ParseSetController(log types.Log) (*GovernorSetController, error) { + event := new(GovernorSetController) + if err := _Governor.contract.UnpackLog(event, "SetController", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// GovernorTimelockChangeIterator is returned from FilterTimelockChange and is used to iterate over the raw logs and unpacked data for TimelockChange events raised by the Governor contract. +type GovernorTimelockChangeIterator struct { + Event *GovernorTimelockChange // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *GovernorTimelockChangeIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(GovernorTimelockChange) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(GovernorTimelockChange) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *GovernorTimelockChangeIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *GovernorTimelockChangeIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// GovernorTimelockChange represents a TimelockChange event raised by the Governor contract. +type GovernorTimelockChange struct { + OldTimelock common.Address + NewTimelock common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterTimelockChange is a free log retrieval operation binding the contract event 0x08f74ea46ef7894f65eabfb5e6e695de773a000b47c529ab559178069b226401. +// +// Solidity: event TimelockChange(address oldTimelock, address newTimelock) +func (_Governor *GovernorFilterer) FilterTimelockChange(opts *bind.FilterOpts) (*GovernorTimelockChangeIterator, error) { + + logs, sub, err := _Governor.contract.FilterLogs(opts, "TimelockChange") + if err != nil { + return nil, err + } + return &GovernorTimelockChangeIterator{contract: _Governor.contract, event: "TimelockChange", logs: logs, sub: sub}, nil +} + +// WatchTimelockChange is a free log subscription operation binding the contract event 0x08f74ea46ef7894f65eabfb5e6e695de773a000b47c529ab559178069b226401. +// +// Solidity: event TimelockChange(address oldTimelock, address newTimelock) +func (_Governor *GovernorFilterer) WatchTimelockChange(opts *bind.WatchOpts, sink chan<- *GovernorTimelockChange) (event.Subscription, error) { + + logs, sub, err := _Governor.contract.WatchLogs(opts, "TimelockChange") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(GovernorTimelockChange) + if err := _Governor.contract.UnpackLog(event, "TimelockChange", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseTimelockChange is a log parse operation binding the contract event 0x08f74ea46ef7894f65eabfb5e6e695de773a000b47c529ab559178069b226401. +// +// Solidity: event TimelockChange(address oldTimelock, address newTimelock) +func (_Governor *GovernorFilterer) ParseTimelockChange(log types.Log) (*GovernorTimelockChange, error) { + event := new(GovernorTimelockChange) + if err := _Governor.contract.UnpackLog(event, "TimelockChange", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// GovernorVoteCastIterator is returned from FilterVoteCast and is used to iterate over the raw logs and unpacked data for VoteCast events raised by the Governor contract. +type GovernorVoteCastIterator struct { + Event *GovernorVoteCast // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *GovernorVoteCastIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(GovernorVoteCast) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(GovernorVoteCast) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *GovernorVoteCastIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *GovernorVoteCastIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// GovernorVoteCast represents a VoteCast event raised by the Governor contract. +type GovernorVoteCast struct { + Voter common.Address + ProposalId *big.Int + Support uint8 + Weight *big.Int + Reason string + Raw types.Log // Blockchain specific contextual infos +} + +// FilterVoteCast is a free log retrieval operation binding the contract event 0xb8e138887d0aa13bab447e82de9d5c1777041ecd21ca36ba824ff1e6c07ddda4. +// +// Solidity: event VoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason) +func (_Governor *GovernorFilterer) FilterVoteCast(opts *bind.FilterOpts, voter []common.Address) (*GovernorVoteCastIterator, error) { + + var voterRule []interface{} + for _, voterItem := range voter { + voterRule = append(voterRule, voterItem) + } + + logs, sub, err := _Governor.contract.FilterLogs(opts, "VoteCast", voterRule) + if err != nil { + return nil, err + } + return &GovernorVoteCastIterator{contract: _Governor.contract, event: "VoteCast", logs: logs, sub: sub}, nil +} + +// WatchVoteCast is a free log subscription operation binding the contract event 0xb8e138887d0aa13bab447e82de9d5c1777041ecd21ca36ba824ff1e6c07ddda4. +// +// Solidity: event VoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason) +func (_Governor *GovernorFilterer) WatchVoteCast(opts *bind.WatchOpts, sink chan<- *GovernorVoteCast, voter []common.Address) (event.Subscription, error) { + + var voterRule []interface{} + for _, voterItem := range voter { + voterRule = append(voterRule, voterItem) + } + + logs, sub, err := _Governor.contract.WatchLogs(opts, "VoteCast", voterRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(GovernorVoteCast) + if err := _Governor.contract.UnpackLog(event, "VoteCast", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseVoteCast is a log parse operation binding the contract event 0xb8e138887d0aa13bab447e82de9d5c1777041ecd21ca36ba824ff1e6c07ddda4. +// +// Solidity: event VoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason) +func (_Governor *GovernorFilterer) ParseVoteCast(log types.Log) (*GovernorVoteCast, error) { + event := new(GovernorVoteCast) + if err := _Governor.contract.UnpackLog(event, "VoteCast", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// GovernorVoteCastWithParamsIterator is returned from FilterVoteCastWithParams and is used to iterate over the raw logs and unpacked data for VoteCastWithParams events raised by the Governor contract. +type GovernorVoteCastWithParamsIterator struct { + Event *GovernorVoteCastWithParams // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *GovernorVoteCastWithParamsIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(GovernorVoteCastWithParams) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(GovernorVoteCastWithParams) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *GovernorVoteCastWithParamsIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *GovernorVoteCastWithParamsIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// GovernorVoteCastWithParams represents a VoteCastWithParams event raised by the Governor contract. +type GovernorVoteCastWithParams struct { + Voter common.Address + ProposalId *big.Int + Support uint8 + Weight *big.Int + Reason string + Params []byte + Raw types.Log // Blockchain specific contextual infos +} + +// FilterVoteCastWithParams is a free log retrieval operation binding the contract event 0xe2babfbac5889a709b63bb7f598b324e08bc5a4fb9ec647fb3cbc9ec07eb8712. +// +// Solidity: event VoteCastWithParams(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason, bytes params) +func (_Governor *GovernorFilterer) FilterVoteCastWithParams(opts *bind.FilterOpts, voter []common.Address) (*GovernorVoteCastWithParamsIterator, error) { + + var voterRule []interface{} + for _, voterItem := range voter { + voterRule = append(voterRule, voterItem) + } + + logs, sub, err := _Governor.contract.FilterLogs(opts, "VoteCastWithParams", voterRule) + if err != nil { + return nil, err + } + return &GovernorVoteCastWithParamsIterator{contract: _Governor.contract, event: "VoteCastWithParams", logs: logs, sub: sub}, nil +} + +// WatchVoteCastWithParams is a free log subscription operation binding the contract event 0xe2babfbac5889a709b63bb7f598b324e08bc5a4fb9ec647fb3cbc9ec07eb8712. +// +// Solidity: event VoteCastWithParams(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason, bytes params) +func (_Governor *GovernorFilterer) WatchVoteCastWithParams(opts *bind.WatchOpts, sink chan<- *GovernorVoteCastWithParams, voter []common.Address) (event.Subscription, error) { + + var voterRule []interface{} + for _, voterItem := range voter { + voterRule = append(voterRule, voterItem) + } + + logs, sub, err := _Governor.contract.WatchLogs(opts, "VoteCastWithParams", voterRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(GovernorVoteCastWithParams) + if err := _Governor.contract.UnpackLog(event, "VoteCastWithParams", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseVoteCastWithParams is a log parse operation binding the contract event 0xe2babfbac5889a709b63bb7f598b324e08bc5a4fb9ec647fb3cbc9ec07eb8712. +// +// Solidity: event VoteCastWithParams(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason, bytes params) +func (_Governor *GovernorFilterer) ParseVoteCastWithParams(log types.Log) (*GovernorVoteCastWithParams, error) { + event := new(GovernorVoteCastWithParams) + if err := _Governor.contract.UnpackLog(event, "VoteCastWithParams", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// GovernorVotingDelaySetIterator is returned from FilterVotingDelaySet and is used to iterate over the raw logs and unpacked data for VotingDelaySet events raised by the Governor contract. +type GovernorVotingDelaySetIterator struct { + Event *GovernorVotingDelaySet // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *GovernorVotingDelaySetIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(GovernorVotingDelaySet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(GovernorVotingDelaySet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *GovernorVotingDelaySetIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *GovernorVotingDelaySetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// GovernorVotingDelaySet represents a VotingDelaySet event raised by the Governor contract. +type GovernorVotingDelaySet struct { + OldVotingDelay *big.Int + NewVotingDelay *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterVotingDelaySet is a free log retrieval operation binding the contract event 0xc565b045403dc03c2eea82b81a0465edad9e2e7fc4d97e11421c209da93d7a93. +// +// Solidity: event VotingDelaySet(uint256 oldVotingDelay, uint256 newVotingDelay) +func (_Governor *GovernorFilterer) FilterVotingDelaySet(opts *bind.FilterOpts) (*GovernorVotingDelaySetIterator, error) { + + logs, sub, err := _Governor.contract.FilterLogs(opts, "VotingDelaySet") + if err != nil { + return nil, err + } + return &GovernorVotingDelaySetIterator{contract: _Governor.contract, event: "VotingDelaySet", logs: logs, sub: sub}, nil +} + +// WatchVotingDelaySet is a free log subscription operation binding the contract event 0xc565b045403dc03c2eea82b81a0465edad9e2e7fc4d97e11421c209da93d7a93. +// +// Solidity: event VotingDelaySet(uint256 oldVotingDelay, uint256 newVotingDelay) +func (_Governor *GovernorFilterer) WatchVotingDelaySet(opts *bind.WatchOpts, sink chan<- *GovernorVotingDelaySet) (event.Subscription, error) { + + logs, sub, err := _Governor.contract.WatchLogs(opts, "VotingDelaySet") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(GovernorVotingDelaySet) + if err := _Governor.contract.UnpackLog(event, "VotingDelaySet", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseVotingDelaySet is a log parse operation binding the contract event 0xc565b045403dc03c2eea82b81a0465edad9e2e7fc4d97e11421c209da93d7a93. +// +// Solidity: event VotingDelaySet(uint256 oldVotingDelay, uint256 newVotingDelay) +func (_Governor *GovernorFilterer) ParseVotingDelaySet(log types.Log) (*GovernorVotingDelaySet, error) { + event := new(GovernorVotingDelaySet) + if err := _Governor.contract.UnpackLog(event, "VotingDelaySet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// GovernorVotingPeriodSetIterator is returned from FilterVotingPeriodSet and is used to iterate over the raw logs and unpacked data for VotingPeriodSet events raised by the Governor contract. +type GovernorVotingPeriodSetIterator struct { + Event *GovernorVotingPeriodSet // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *GovernorVotingPeriodSetIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(GovernorVotingPeriodSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(GovernorVotingPeriodSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *GovernorVotingPeriodSetIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *GovernorVotingPeriodSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// GovernorVotingPeriodSet represents a VotingPeriodSet event raised by the Governor contract. +type GovernorVotingPeriodSet struct { + OldVotingPeriod *big.Int + NewVotingPeriod *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterVotingPeriodSet is a free log retrieval operation binding the contract event 0x7e3f7f0708a84de9203036abaa450dccc85ad5ff52f78c170f3edb55cf5e8828. +// +// Solidity: event VotingPeriodSet(uint256 oldVotingPeriod, uint256 newVotingPeriod) +func (_Governor *GovernorFilterer) FilterVotingPeriodSet(opts *bind.FilterOpts) (*GovernorVotingPeriodSetIterator, error) { + + logs, sub, err := _Governor.contract.FilterLogs(opts, "VotingPeriodSet") + if err != nil { + return nil, err + } + return &GovernorVotingPeriodSetIterator{contract: _Governor.contract, event: "VotingPeriodSet", logs: logs, sub: sub}, nil +} + +// WatchVotingPeriodSet is a free log subscription operation binding the contract event 0x7e3f7f0708a84de9203036abaa450dccc85ad5ff52f78c170f3edb55cf5e8828. +// +// Solidity: event VotingPeriodSet(uint256 oldVotingPeriod, uint256 newVotingPeriod) +func (_Governor *GovernorFilterer) WatchVotingPeriodSet(opts *bind.WatchOpts, sink chan<- *GovernorVotingPeriodSet) (event.Subscription, error) { + + logs, sub, err := _Governor.contract.WatchLogs(opts, "VotingPeriodSet") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(GovernorVotingPeriodSet) + if err := _Governor.contract.UnpackLog(event, "VotingPeriodSet", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseVotingPeriodSet is a log parse operation binding the contract event 0x7e3f7f0708a84de9203036abaa450dccc85ad5ff52f78c170f3edb55cf5e8828. +// +// Solidity: event VotingPeriodSet(uint256 oldVotingPeriod, uint256 newVotingPeriod) +func (_Governor *GovernorFilterer) ParseVotingPeriodSet(log types.Log) (*GovernorVotingPeriodSet, error) { + event := new(GovernorVotingPeriodSet) + if err := _Governor.contract.UnpackLog(event, "VotingPeriodSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/eth/contracts/bondingManager.go b/eth/contracts/bondingManager.go index f04e33c95c..8dcd1a2538 100644 --- a/eth/contracts/bondingManager.go +++ b/eth/contracts/bondingManager.go @@ -26,11 +26,12 @@ var ( _ = common.Big1 _ = types.BloomLookup _ = event.NewSubscription + _ = abi.ConvertType ) // BondingManagerMetaData contains all meta data concerning the BondingManager contract. var BondingManagerMetaData = &bind.MetaData{ - ABI: "[{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_unbondingLockId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_newPosPrev\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_newPosNext\",\"type\":\"address\"}],\"name\":\"rebondFromUnbondedWithHint\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_delegator\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_oldDelegateNewPosPrev\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_oldDelegateNewPosNext\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_newDelegateNewPosPrev\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_newDelegateNewPosNext\",\"type\":\"address\"}],\"name\":\"transferBond\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_transcoder\",\"type\":\"address\"}],\"name\":\"isActiveTranscoder\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_delegator\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_unbondingLockId\",\"type\":\"uint256\"}],\"name\":\"isValidUnbondingLock\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_delegator\",\"type\":\"address\"}],\"name\":\"delegatorStatus\",\"outputs\":[{\"internalType\":\"enumBondingManager.DelegatorStatus\",\"name\":\"\",\"type\":\"uint8\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"reward\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_transcoder\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_finder\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_slashAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_finderFee\",\"type\":\"uint256\"}],\"name\":\"slashTranscoder\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_transcoder\",\"type\":\"address\"}],\"name\":\"getNextTranscoderInPool\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_transcoder\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_round\",\"type\":\"uint256\"}],\"name\":\"getTranscoderEarningsPoolForRound\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"totalStake\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"transcoderRewardCut\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"transcoderFeeShare\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"cumulativeRewardFactor\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"cumulativeFeeFactor\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_endRound\",\"type\":\"uint256\"}],\"name\":\"claimEarnings\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_unbondingLockId\",\"type\":\"uint256\"}],\"name\":\"withdrawStake\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"unbond\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"getTranscoderPoolSize\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_rewardCut\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_feeShare\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_newPosPrev\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_newPosNext\",\"type\":\"address\"}],\"name\":\"transcoderWithHint\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_unbondingLockId\",\"type\":\"uint256\"}],\"name\":\"rebondFromUnbonded\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_transcoder\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_fees\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_round\",\"type\":\"uint256\"}],\"name\":\"updateTranscoderWithFees\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_delegator\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_unbondingLockId\",\"type\":\"uint256\"}],\"name\":\"getDelegatorUnbondingLock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"withdrawRound\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"currentRoundTotalActiveStake\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_rewardCut\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_feeShare\",\"type\":\"uint256\"}],\"name\":\"transcoder\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"nextRoundTotalActiveStake\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"targetContractId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"getTranscoderPoolMaxSize\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"getTotalBonded\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_transcoder\",\"type\":\"address\"}],\"name\":\"getTranscoder\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"lastRewardRound\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"rewardCut\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"feeShare\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lastActiveStakeUpdateRound\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"activationRound\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"deactivationRound\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"activeCumulativeRewards\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"cumulativeRewards\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"cumulativeFees\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lastFeeRound\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_numActiveTranscoders\",\"type\":\"uint256\"}],\"name\":\"setNumActiveTranscoders\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_transcoder\",\"type\":\"address\"}],\"name\":\"isRegisteredTranscoder\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_oldDelegateNewPosPrev\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_oldDelegateNewPosNext\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_currDelegateNewPosPrev\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_currDelegateNewPosNext\",\"type\":\"address\"}],\"name\":\"bondWithHint\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"unbondingPeriod\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"setCurrentRoundTotalActiveStake\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_unbondingLockId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_newPosPrev\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_newPosNext\",\"type\":\"address\"}],\"name\":\"rebondWithHint\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newPosPrev\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_newPosNext\",\"type\":\"address\"}],\"name\":\"rewardWithHint\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"getFirstTranscoderInPool\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_transcoder\",\"type\":\"address\"}],\"name\":\"transcoderStatus\",\"outputs\":[{\"internalType\":\"enumBondingManager.TranscoderStatus\",\"name\":\"\",\"type\":\"uint8\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_controller\",\"type\":\"address\"}],\"name\":\"setController\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_newPosPrev\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_newPosNext\",\"type\":\"address\"}],\"name\":\"unbondWithHint\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_delegator\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_endRound\",\"type\":\"uint256\"}],\"name\":\"pendingStake\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_transcoder\",\"type\":\"address\"}],\"name\":\"transcoderTotalStake\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_delegator\",\"type\":\"address\"}],\"name\":\"getDelegator\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"bondedAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fees\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"delegateAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"delegatedAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"startRound\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lastClaimRound\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nextUnbondingLockId\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"addresspayable\",\"name\":\"_recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"withdrawFees\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"}],\"name\":\"bond\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_unbondingLockId\",\"type\":\"uint256\"}],\"name\":\"rebond\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_oldDelegateNewPosPrev\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_oldDelegateNewPosNext\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_currDelegateNewPosPrev\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_currDelegateNewPosNext\",\"type\":\"address\"}],\"name\":\"bondForWithHint\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"_unbondingPeriod\",\"type\":\"uint64\"}],\"name\":\"setUnbondingPeriod\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_delegator\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_endRound\",\"type\":\"uint256\"}],\"name\":\"pendingFees\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"controller\",\"outputs\":[{\"internalType\":\"contractIController\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_controller\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"rewardCut\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"feeShare\",\"type\":\"uint256\"}],\"name\":\"TranscoderUpdate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"activationRound\",\"type\":\"uint256\"}],\"name\":\"TranscoderActivated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"deactivationRound\",\"type\":\"uint256\"}],\"name\":\"TranscoderDeactivated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"finder\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"penalty\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"finderReward\",\"type\":\"uint256\"}],\"name\":\"TranscoderSlashed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Reward\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newDelegate\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oldDelegate\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"additionalAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"bondedAmount\",\"type\":\"uint256\"}],\"name\":\"Bond\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegate\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"unbondingLockId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"withdrawRound\",\"type\":\"uint256\"}],\"name\":\"Unbond\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegate\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"unbondingLockId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Rebond\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"unbondingLockId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"withdrawRound\",\"type\":\"uint256\"}],\"name\":\"WithdrawStake\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"WithdrawFees\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegate\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"rewards\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fees\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"startRound\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"endRound\",\"type\":\"uint256\"}],\"name\":\"EarningsClaimed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"controller\",\"type\":\"address\"}],\"name\":\"SetController\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"param\",\"type\":\"string\"}],\"name\":\"ParameterUpdate\",\"type\":\"event\"}]", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_controller\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newDelegate\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oldDelegate\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"additionalAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"bondedAmount\",\"type\":\"uint256\"}],\"name\":\"Bond\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegate\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"rewards\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fees\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"startRound\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"endRound\",\"type\":\"uint256\"}],\"name\":\"EarningsClaimed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"param\",\"type\":\"string\"}],\"name\":\"ParameterUpdate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegate\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"unbondingLockId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Rebond\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Reward\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"controller\",\"type\":\"address\"}],\"name\":\"SetController\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"activationRound\",\"type\":\"uint256\"}],\"name\":\"TranscoderActivated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"deactivationRound\",\"type\":\"uint256\"}],\"name\":\"TranscoderDeactivated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"finder\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"penalty\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"finderReward\",\"type\":\"uint256\"}],\"name\":\"TranscoderSlashed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"rewardCut\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"feeShare\",\"type\":\"uint256\"}],\"name\":\"TranscoderUpdate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oldDelegator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newDelegator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldUnbondingLockId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newUnbondingLockId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"TransferBond\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"treasury\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"TreasuryReward\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegate\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"unbondingLockId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"withdrawRound\",\"type\":\"uint256\"}],\"name\":\"Unbond\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"WithdrawFees\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"unbondingLockId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"withdrawRound\",\"type\":\"uint256\"}],\"name\":\"WithdrawStake\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"}],\"name\":\"bond\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_oldDelegateNewPosPrev\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_oldDelegateNewPosNext\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_currDelegateNewPosPrev\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_currDelegateNewPosNext\",\"type\":\"address\"}],\"name\":\"bondForWithHint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_oldDelegateNewPosPrev\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_oldDelegateNewPosNext\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_currDelegateNewPosPrev\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_currDelegateNewPosNext\",\"type\":\"address\"}],\"name\":\"bondWithHint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_account\",\"type\":\"address\"}],\"name\":\"checkpointBondingState\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_endRound\",\"type\":\"uint256\"}],\"name\":\"claimEarnings\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"controller\",\"outputs\":[{\"internalType\":\"contractIController\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"currentRoundTotalActiveStake\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_delegator\",\"type\":\"address\"}],\"name\":\"delegatorStatus\",\"outputs\":[{\"internalType\":\"enumBondingManager.DelegatorStatus\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_delegator\",\"type\":\"address\"}],\"name\":\"getDelegator\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"bondedAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fees\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"delegateAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"delegatedAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"startRound\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lastClaimRound\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nextUnbondingLockId\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_delegator\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_unbondingLockId\",\"type\":\"uint256\"}],\"name\":\"getDelegatorUnbondingLock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"withdrawRound\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getFirstTranscoderInPool\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_transcoder\",\"type\":\"address\"}],\"name\":\"getNextTranscoderInPool\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getTotalBonded\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_transcoder\",\"type\":\"address\"}],\"name\":\"getTranscoder\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"lastRewardRound\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"rewardCut\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"feeShare\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lastActiveStakeUpdateRound\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"activationRound\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"deactivationRound\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"activeCumulativeRewards\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"cumulativeRewards\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"cumulativeFees\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lastFeeRound\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_transcoder\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_round\",\"type\":\"uint256\"}],\"name\":\"getTranscoderEarningsPoolForRound\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"totalStake\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"transcoderRewardCut\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"transcoderFeeShare\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"cumulativeRewardFactor\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"cumulativeFeeFactor\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getTranscoderPoolMaxSize\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getTranscoderPoolSize\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_transcoder\",\"type\":\"address\"}],\"name\":\"isActiveTranscoder\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_transcoder\",\"type\":\"address\"}],\"name\":\"isRegisteredTranscoder\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_delegator\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_unbondingLockId\",\"type\":\"uint256\"}],\"name\":\"isValidUnbondingLock\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"nextRoundTotalActiveStake\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"nextRoundTreasuryRewardCutRate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_delegator\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_endRound\",\"type\":\"uint256\"}],\"name\":\"pendingFees\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_delegator\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_endRound\",\"type\":\"uint256\"}],\"name\":\"pendingStake\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_unbondingLockId\",\"type\":\"uint256\"}],\"name\":\"rebond\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_unbondingLockId\",\"type\":\"uint256\"}],\"name\":\"rebondFromUnbonded\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_unbondingLockId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_newPosPrev\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_newPosNext\",\"type\":\"address\"}],\"name\":\"rebondFromUnbondedWithHint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_unbondingLockId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_newPosPrev\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_newPosNext\",\"type\":\"address\"}],\"name\":\"rebondWithHint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"reward\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newPosPrev\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_newPosNext\",\"type\":\"address\"}],\"name\":\"rewardWithHint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_controller\",\"type\":\"address\"}],\"name\":\"setController\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"setCurrentRoundTotalActiveStake\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_numActiveTranscoders\",\"type\":\"uint256\"}],\"name\":\"setNumActiveTranscoders\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_ceiling\",\"type\":\"uint256\"}],\"name\":\"setTreasuryBalanceCeiling\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_cutRate\",\"type\":\"uint256\"}],\"name\":\"setTreasuryRewardCutRate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"_unbondingPeriod\",\"type\":\"uint64\"}],\"name\":\"setUnbondingPeriod\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_transcoder\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_finder\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_slashAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_finderFee\",\"type\":\"uint256\"}],\"name\":\"slashTranscoder\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"targetContractId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_rewardCut\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_feeShare\",\"type\":\"uint256\"}],\"name\":\"transcoder\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_transcoder\",\"type\":\"address\"}],\"name\":\"transcoderStatus\",\"outputs\":[{\"internalType\":\"enumBondingManager.TranscoderStatus\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_transcoder\",\"type\":\"address\"}],\"name\":\"transcoderTotalStake\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_rewardCut\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_feeShare\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_newPosPrev\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_newPosNext\",\"type\":\"address\"}],\"name\":\"transcoderWithHint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_delegator\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_oldDelegateNewPosPrev\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_oldDelegateNewPosNext\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_newDelegateNewPosPrev\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_newDelegateNewPosNext\",\"type\":\"address\"}],\"name\":\"transferBond\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"treasuryBalanceCeiling\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"treasuryRewardCutRate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"unbond\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_newPosPrev\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_newPosNext\",\"type\":\"address\"}],\"name\":\"unbondWithHint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unbondingPeriod\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_transcoder\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_fees\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_round\",\"type\":\"uint256\"}],\"name\":\"updateTranscoderWithFees\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"addresspayable\",\"name\":\"_recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"withdrawFees\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_unbondingLockId\",\"type\":\"uint256\"}],\"name\":\"withdrawStake\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", } // BondingManagerABI is the input ABI used to generate the binding from. @@ -134,11 +135,11 @@ func NewBondingManagerFilterer(address common.Address, filterer bind.ContractFil // bindBondingManager binds a generic wrapper to an already deployed contract. func bindBondingManager(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { - parsed, err := abi.JSON(strings.NewReader(BondingManagerABI)) + parsed, err := BondingManagerMetaData.GetAbi() if err != nil { return nil, err } - return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil } // Call invokes the (constant) contract method with params as input values and @@ -811,6 +812,37 @@ func (_BondingManager *BondingManagerCallerSession) NextRoundTotalActiveStake() return _BondingManager.Contract.NextRoundTotalActiveStake(&_BondingManager.CallOpts) } +// NextRoundTreasuryRewardCutRate is a free data retrieval call binding the contract method 0xa5766231. +// +// Solidity: function nextRoundTreasuryRewardCutRate() view returns(uint256) +func (_BondingManager *BondingManagerCaller) NextRoundTreasuryRewardCutRate(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _BondingManager.contract.Call(opts, &out, "nextRoundTreasuryRewardCutRate") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// NextRoundTreasuryRewardCutRate is a free data retrieval call binding the contract method 0xa5766231. +// +// Solidity: function nextRoundTreasuryRewardCutRate() view returns(uint256) +func (_BondingManager *BondingManagerSession) NextRoundTreasuryRewardCutRate() (*big.Int, error) { + return _BondingManager.Contract.NextRoundTreasuryRewardCutRate(&_BondingManager.CallOpts) +} + +// NextRoundTreasuryRewardCutRate is a free data retrieval call binding the contract method 0xa5766231. +// +// Solidity: function nextRoundTreasuryRewardCutRate() view returns(uint256) +func (_BondingManager *BondingManagerCallerSession) NextRoundTreasuryRewardCutRate() (*big.Int, error) { + return _BondingManager.Contract.NextRoundTreasuryRewardCutRate(&_BondingManager.CallOpts) +} + // PendingFees is a free data retrieval call binding the contract method 0xf595f1cc. // // Solidity: function pendingFees(address _delegator, uint256 _endRound) view returns(uint256) @@ -966,6 +998,68 @@ func (_BondingManager *BondingManagerCallerSession) TranscoderTotalStake(_transc return _BondingManager.Contract.TranscoderTotalStake(&_BondingManager.CallOpts, _transcoder) } +// TreasuryBalanceCeiling is a free data retrieval call binding the contract method 0x84a16bbc. +// +// Solidity: function treasuryBalanceCeiling() view returns(uint256) +func (_BondingManager *BondingManagerCaller) TreasuryBalanceCeiling(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _BondingManager.contract.Call(opts, &out, "treasuryBalanceCeiling") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// TreasuryBalanceCeiling is a free data retrieval call binding the contract method 0x84a16bbc. +// +// Solidity: function treasuryBalanceCeiling() view returns(uint256) +func (_BondingManager *BondingManagerSession) TreasuryBalanceCeiling() (*big.Int, error) { + return _BondingManager.Contract.TreasuryBalanceCeiling(&_BondingManager.CallOpts) +} + +// TreasuryBalanceCeiling is a free data retrieval call binding the contract method 0x84a16bbc. +// +// Solidity: function treasuryBalanceCeiling() view returns(uint256) +func (_BondingManager *BondingManagerCallerSession) TreasuryBalanceCeiling() (*big.Int, error) { + return _BondingManager.Contract.TreasuryBalanceCeiling(&_BondingManager.CallOpts) +} + +// TreasuryRewardCutRate is a free data retrieval call binding the contract method 0x558f8f44. +// +// Solidity: function treasuryRewardCutRate() view returns(uint256) +func (_BondingManager *BondingManagerCaller) TreasuryRewardCutRate(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _BondingManager.contract.Call(opts, &out, "treasuryRewardCutRate") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// TreasuryRewardCutRate is a free data retrieval call binding the contract method 0x558f8f44. +// +// Solidity: function treasuryRewardCutRate() view returns(uint256) +func (_BondingManager *BondingManagerSession) TreasuryRewardCutRate() (*big.Int, error) { + return _BondingManager.Contract.TreasuryRewardCutRate(&_BondingManager.CallOpts) +} + +// TreasuryRewardCutRate is a free data retrieval call binding the contract method 0x558f8f44. +// +// Solidity: function treasuryRewardCutRate() view returns(uint256) +func (_BondingManager *BondingManagerCallerSession) TreasuryRewardCutRate() (*big.Int, error) { + return _BondingManager.Contract.TreasuryRewardCutRate(&_BondingManager.CallOpts) +} + // UnbondingPeriod is a free data retrieval call binding the contract method 0x6cf6d675. // // Solidity: function unbondingPeriod() view returns(uint64) @@ -1060,6 +1154,27 @@ func (_BondingManager *BondingManagerTransactorSession) BondWithHint(_amount *bi return _BondingManager.Contract.BondWithHint(&_BondingManager.TransactOpts, _amount, _to, _oldDelegateNewPosPrev, _oldDelegateNewPosNext, _currDelegateNewPosPrev, _currDelegateNewPosNext) } +// CheckpointBondingState is a paid mutator transaction binding the contract method 0xd52bd62b. +// +// Solidity: function checkpointBondingState(address _account) returns() +func (_BondingManager *BondingManagerTransactor) CheckpointBondingState(opts *bind.TransactOpts, _account common.Address) (*types.Transaction, error) { + return _BondingManager.contract.Transact(opts, "checkpointBondingState", _account) +} + +// CheckpointBondingState is a paid mutator transaction binding the contract method 0xd52bd62b. +// +// Solidity: function checkpointBondingState(address _account) returns() +func (_BondingManager *BondingManagerSession) CheckpointBondingState(_account common.Address) (*types.Transaction, error) { + return _BondingManager.Contract.CheckpointBondingState(&_BondingManager.TransactOpts, _account) +} + +// CheckpointBondingState is a paid mutator transaction binding the contract method 0xd52bd62b. +// +// Solidity: function checkpointBondingState(address _account) returns() +func (_BondingManager *BondingManagerTransactorSession) CheckpointBondingState(_account common.Address) (*types.Transaction, error) { + return _BondingManager.Contract.CheckpointBondingState(&_BondingManager.TransactOpts, _account) +} + // ClaimEarnings is a paid mutator transaction binding the contract method 0x24b1babf. // // Solidity: function claimEarnings(uint256 _endRound) returns() @@ -1270,6 +1385,48 @@ func (_BondingManager *BondingManagerTransactorSession) SetNumActiveTranscoders( return _BondingManager.Contract.SetNumActiveTranscoders(&_BondingManager.TransactOpts, _numActiveTranscoders) } +// SetTreasuryBalanceCeiling is a paid mutator transaction binding the contract method 0x40e6f271. +// +// Solidity: function setTreasuryBalanceCeiling(uint256 _ceiling) returns() +func (_BondingManager *BondingManagerTransactor) SetTreasuryBalanceCeiling(opts *bind.TransactOpts, _ceiling *big.Int) (*types.Transaction, error) { + return _BondingManager.contract.Transact(opts, "setTreasuryBalanceCeiling", _ceiling) +} + +// SetTreasuryBalanceCeiling is a paid mutator transaction binding the contract method 0x40e6f271. +// +// Solidity: function setTreasuryBalanceCeiling(uint256 _ceiling) returns() +func (_BondingManager *BondingManagerSession) SetTreasuryBalanceCeiling(_ceiling *big.Int) (*types.Transaction, error) { + return _BondingManager.Contract.SetTreasuryBalanceCeiling(&_BondingManager.TransactOpts, _ceiling) +} + +// SetTreasuryBalanceCeiling is a paid mutator transaction binding the contract method 0x40e6f271. +// +// Solidity: function setTreasuryBalanceCeiling(uint256 _ceiling) returns() +func (_BondingManager *BondingManagerTransactorSession) SetTreasuryBalanceCeiling(_ceiling *big.Int) (*types.Transaction, error) { + return _BondingManager.Contract.SetTreasuryBalanceCeiling(&_BondingManager.TransactOpts, _ceiling) +} + +// SetTreasuryRewardCutRate is a paid mutator transaction binding the contract method 0xcf33a0d5. +// +// Solidity: function setTreasuryRewardCutRate(uint256 _cutRate) returns() +func (_BondingManager *BondingManagerTransactor) SetTreasuryRewardCutRate(opts *bind.TransactOpts, _cutRate *big.Int) (*types.Transaction, error) { + return _BondingManager.contract.Transact(opts, "setTreasuryRewardCutRate", _cutRate) +} + +// SetTreasuryRewardCutRate is a paid mutator transaction binding the contract method 0xcf33a0d5. +// +// Solidity: function setTreasuryRewardCutRate(uint256 _cutRate) returns() +func (_BondingManager *BondingManagerSession) SetTreasuryRewardCutRate(_cutRate *big.Int) (*types.Transaction, error) { + return _BondingManager.Contract.SetTreasuryRewardCutRate(&_BondingManager.TransactOpts, _cutRate) +} + +// SetTreasuryRewardCutRate is a paid mutator transaction binding the contract method 0xcf33a0d5. +// +// Solidity: function setTreasuryRewardCutRate(uint256 _cutRate) returns() +func (_BondingManager *BondingManagerTransactorSession) SetTreasuryRewardCutRate(_cutRate *big.Int) (*types.Transaction, error) { + return _BondingManager.Contract.SetTreasuryRewardCutRate(&_BondingManager.TransactOpts, _cutRate) +} + // SetUnbondingPeriod is a paid mutator transaction binding the contract method 0xf10d1de1. // // Solidity: function setUnbondingPeriod(uint64 _unbondingPeriod) returns() @@ -2952,6 +3109,308 @@ func (_BondingManager *BondingManagerFilterer) ParseTranscoderUpdate(log types.L return event, nil } +// BondingManagerTransferBondIterator is returned from FilterTransferBond and is used to iterate over the raw logs and unpacked data for TransferBond events raised by the BondingManager contract. +type BondingManagerTransferBondIterator struct { + Event *BondingManagerTransferBond // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BondingManagerTransferBondIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BondingManagerTransferBond) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BondingManagerTransferBond) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BondingManagerTransferBondIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BondingManagerTransferBondIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BondingManagerTransferBond represents a TransferBond event raised by the BondingManager contract. +type BondingManagerTransferBond struct { + OldDelegator common.Address + NewDelegator common.Address + OldUnbondingLockId *big.Int + NewUnbondingLockId *big.Int + Amount *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterTransferBond is a free log retrieval operation binding the contract event 0xf136b986590e86cf1abd7b6600186a7a1178ad3cbbdf0f3312e79f6214a2a567. +// +// Solidity: event TransferBond(address indexed oldDelegator, address indexed newDelegator, uint256 oldUnbondingLockId, uint256 newUnbondingLockId, uint256 amount) +func (_BondingManager *BondingManagerFilterer) FilterTransferBond(opts *bind.FilterOpts, oldDelegator []common.Address, newDelegator []common.Address) (*BondingManagerTransferBondIterator, error) { + + var oldDelegatorRule []interface{} + for _, oldDelegatorItem := range oldDelegator { + oldDelegatorRule = append(oldDelegatorRule, oldDelegatorItem) + } + var newDelegatorRule []interface{} + for _, newDelegatorItem := range newDelegator { + newDelegatorRule = append(newDelegatorRule, newDelegatorItem) + } + + logs, sub, err := _BondingManager.contract.FilterLogs(opts, "TransferBond", oldDelegatorRule, newDelegatorRule) + if err != nil { + return nil, err + } + return &BondingManagerTransferBondIterator{contract: _BondingManager.contract, event: "TransferBond", logs: logs, sub: sub}, nil +} + +// WatchTransferBond is a free log subscription operation binding the contract event 0xf136b986590e86cf1abd7b6600186a7a1178ad3cbbdf0f3312e79f6214a2a567. +// +// Solidity: event TransferBond(address indexed oldDelegator, address indexed newDelegator, uint256 oldUnbondingLockId, uint256 newUnbondingLockId, uint256 amount) +func (_BondingManager *BondingManagerFilterer) WatchTransferBond(opts *bind.WatchOpts, sink chan<- *BondingManagerTransferBond, oldDelegator []common.Address, newDelegator []common.Address) (event.Subscription, error) { + + var oldDelegatorRule []interface{} + for _, oldDelegatorItem := range oldDelegator { + oldDelegatorRule = append(oldDelegatorRule, oldDelegatorItem) + } + var newDelegatorRule []interface{} + for _, newDelegatorItem := range newDelegator { + newDelegatorRule = append(newDelegatorRule, newDelegatorItem) + } + + logs, sub, err := _BondingManager.contract.WatchLogs(opts, "TransferBond", oldDelegatorRule, newDelegatorRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BondingManagerTransferBond) + if err := _BondingManager.contract.UnpackLog(event, "TransferBond", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseTransferBond is a log parse operation binding the contract event 0xf136b986590e86cf1abd7b6600186a7a1178ad3cbbdf0f3312e79f6214a2a567. +// +// Solidity: event TransferBond(address indexed oldDelegator, address indexed newDelegator, uint256 oldUnbondingLockId, uint256 newUnbondingLockId, uint256 amount) +func (_BondingManager *BondingManagerFilterer) ParseTransferBond(log types.Log) (*BondingManagerTransferBond, error) { + event := new(BondingManagerTransferBond) + if err := _BondingManager.contract.UnpackLog(event, "TransferBond", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BondingManagerTreasuryRewardIterator is returned from FilterTreasuryReward and is used to iterate over the raw logs and unpacked data for TreasuryReward events raised by the BondingManager contract. +type BondingManagerTreasuryRewardIterator struct { + Event *BondingManagerTreasuryReward // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BondingManagerTreasuryRewardIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BondingManagerTreasuryReward) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BondingManagerTreasuryReward) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BondingManagerTreasuryRewardIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BondingManagerTreasuryRewardIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BondingManagerTreasuryReward represents a TreasuryReward event raised by the BondingManager contract. +type BondingManagerTreasuryReward struct { + Transcoder common.Address + Treasury common.Address + Amount *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterTreasuryReward is a free log retrieval operation binding the contract event 0x40e200718fe552021167687e5491d09f5931275b6f88f14a462650f0effb5237. +// +// Solidity: event TreasuryReward(address indexed transcoder, address treasury, uint256 amount) +func (_BondingManager *BondingManagerFilterer) FilterTreasuryReward(opts *bind.FilterOpts, transcoder []common.Address) (*BondingManagerTreasuryRewardIterator, error) { + + var transcoderRule []interface{} + for _, transcoderItem := range transcoder { + transcoderRule = append(transcoderRule, transcoderItem) + } + + logs, sub, err := _BondingManager.contract.FilterLogs(opts, "TreasuryReward", transcoderRule) + if err != nil { + return nil, err + } + return &BondingManagerTreasuryRewardIterator{contract: _BondingManager.contract, event: "TreasuryReward", logs: logs, sub: sub}, nil +} + +// WatchTreasuryReward is a free log subscription operation binding the contract event 0x40e200718fe552021167687e5491d09f5931275b6f88f14a462650f0effb5237. +// +// Solidity: event TreasuryReward(address indexed transcoder, address treasury, uint256 amount) +func (_BondingManager *BondingManagerFilterer) WatchTreasuryReward(opts *bind.WatchOpts, sink chan<- *BondingManagerTreasuryReward, transcoder []common.Address) (event.Subscription, error) { + + var transcoderRule []interface{} + for _, transcoderItem := range transcoder { + transcoderRule = append(transcoderRule, transcoderItem) + } + + logs, sub, err := _BondingManager.contract.WatchLogs(opts, "TreasuryReward", transcoderRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BondingManagerTreasuryReward) + if err := _BondingManager.contract.UnpackLog(event, "TreasuryReward", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseTreasuryReward is a log parse operation binding the contract event 0x40e200718fe552021167687e5491d09f5931275b6f88f14a462650f0effb5237. +// +// Solidity: event TreasuryReward(address indexed transcoder, address treasury, uint256 amount) +func (_BondingManager *BondingManagerFilterer) ParseTreasuryReward(log types.Log) (*BondingManagerTreasuryReward, error) { + event := new(BondingManagerTreasuryReward) + if err := _BondingManager.contract.UnpackLog(event, "TreasuryReward", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + // BondingManagerUnbondIterator is returned from FilterUnbond and is used to iterate over the raw logs and unpacked data for Unbond events raised by the BondingManager contract. type BondingManagerUnbondIterator struct { Event *BondingManagerUnbond // Event containing the contract specifics and raw log diff --git a/eth/contracts/controller.go b/eth/contracts/controller.go index 56de908a5e..f1135e5a32 100644 --- a/eth/contracts/controller.go +++ b/eth/contracts/controller.go @@ -26,11 +26,12 @@ var ( _ = common.Big1 _ = types.BloomLookup _ = event.NewSubscription + _ = abi.ConvertType ) // ControllerMetaData contains all meta data concerning the Controller contract. var ControllerMetaData = &bind.MetaData{ - ABI: "[{\"constant\":false,\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_id\",\"type\":\"bytes32\"}],\"name\":\"getContractInfo\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes20\",\"name\":\"\",\"type\":\"bytes20\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_id\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"_contractAddress\",\"type\":\"address\"},{\"internalType\":\"bytes20\",\"name\":\"_gitCommitHash\",\"type\":\"bytes20\"}],\"name\":\"setContractInfo\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_id\",\"type\":\"bytes32\"}],\"name\":\"getContract\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_id\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"_controller\",\"type\":\"address\"}],\"name\":\"updateController\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"contractAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes20\",\"name\":\"gitCommitHash\",\"type\":\"bytes20\"}],\"name\":\"SetContractInfo\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"Pause\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"Unpause\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"}]", + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"Pause\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"contractAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes20\",\"name\":\"gitCommitHash\",\"type\":\"bytes20\"}],\"name\":\"SetContractInfo\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"Unpause\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_id\",\"type\":\"bytes32\"}],\"name\":\"getContract\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_id\",\"type\":\"bytes32\"}],\"name\":\"getContractInfo\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes20\",\"name\":\"\",\"type\":\"bytes20\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_id\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"_contractAddress\",\"type\":\"address\"},{\"internalType\":\"bytes20\",\"name\":\"_gitCommitHash\",\"type\":\"bytes20\"}],\"name\":\"setContractInfo\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_id\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"_controller\",\"type\":\"address\"}],\"name\":\"updateController\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", } // ControllerABI is the input ABI used to generate the binding from. @@ -134,11 +135,11 @@ func NewControllerFilterer(address common.Address, filterer bind.ContractFiltere // bindController binds a generic wrapper to an already deployed contract. func bindController(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { - parsed, err := abi.JSON(strings.NewReader(ControllerABI)) + parsed, err := ControllerMetaData.GetAbi() if err != nil { return nil, err } - return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil } // Call invokes the (constant) contract method with params as input values and diff --git a/eth/contracts/livepeerToken.go b/eth/contracts/livepeerToken.go index 25ed2f90cc..3c119170e5 100644 --- a/eth/contracts/livepeerToken.go +++ b/eth/contracts/livepeerToken.go @@ -26,11 +26,12 @@ var ( _ = common.Big1 _ = types.BloomLookup _ = event.NewSubscription + _ = abi.ConvertType ) // LivepeerTokenMetaData contains all meta data concerning the LivepeerToken contract. var LivepeerTokenMetaData = &bind.MetaData{ - ABI: "[{\"constant\":true,\"inputs\":[],\"name\":\"mintingFinished\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"addedValue\",\"type\":\"uint256\"}],\"name\":\"increaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"version\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"finishMinting\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"subtractedValue\",\"type\":\"uint256\"}],\"name\":\"decreaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"burner\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Burn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Mint\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"MintFinished\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"}]", + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"InvalidShortString\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"str\",\"type\":\"string\"}],\"name\":\"StringTooLong\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"burner\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Burn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"EIP712DomainChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Mint\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"previousAdminRole\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"newAdminRole\",\"type\":\"bytes32\"}],\"name\":\"RoleAdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleRevoked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"DEFAULT_ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"DOMAIN_SEPARATOR\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_from\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"burnFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"subtractedValue\",\"type\":\"uint256\"}],\"name\":\"decreaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"eip712Domain\",\"outputs\":[{\"internalType\":\"bytes1\",\"name\":\"fields\",\"type\":\"bytes1\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"version\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"verifyingContract\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"salt\",\"type\":\"bytes32\"},{\"internalType\":\"uint256[]\",\"name\":\"extensions\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"}],\"name\":\"getRoleAdmin\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"grantRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"hasRole\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"addedValue\",\"type\":\"uint256\"}],\"name\":\"increaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"nonces\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"deadline\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"permit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"renounceRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"revokeRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", } // LivepeerTokenABI is the input ABI used to generate the binding from. @@ -134,11 +135,11 @@ func NewLivepeerTokenFilterer(address common.Address, filterer bind.ContractFilt // bindLivepeerToken binds a generic wrapper to an already deployed contract. func bindLivepeerToken(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { - parsed, err := abi.JSON(strings.NewReader(LivepeerTokenABI)) + parsed, err := LivepeerTokenMetaData.GetAbi() if err != nil { return nil, err } - return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil } // Call invokes the (constant) contract method with params as input values and @@ -179,6 +180,68 @@ func (_LivepeerToken *LivepeerTokenTransactorRaw) Transact(opts *bind.TransactOp return _LivepeerToken.Contract.contract.Transact(opts, method, params...) } +// DEFAULTADMINROLE is a free data retrieval call binding the contract method 0xa217fddf. +// +// Solidity: function DEFAULT_ADMIN_ROLE() view returns(bytes32) +func (_LivepeerToken *LivepeerTokenCaller) DEFAULTADMINROLE(opts *bind.CallOpts) ([32]byte, error) { + var out []interface{} + err := _LivepeerToken.contract.Call(opts, &out, "DEFAULT_ADMIN_ROLE") + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// DEFAULTADMINROLE is a free data retrieval call binding the contract method 0xa217fddf. +// +// Solidity: function DEFAULT_ADMIN_ROLE() view returns(bytes32) +func (_LivepeerToken *LivepeerTokenSession) DEFAULTADMINROLE() ([32]byte, error) { + return _LivepeerToken.Contract.DEFAULTADMINROLE(&_LivepeerToken.CallOpts) +} + +// DEFAULTADMINROLE is a free data retrieval call binding the contract method 0xa217fddf. +// +// Solidity: function DEFAULT_ADMIN_ROLE() view returns(bytes32) +func (_LivepeerToken *LivepeerTokenCallerSession) DEFAULTADMINROLE() ([32]byte, error) { + return _LivepeerToken.Contract.DEFAULTADMINROLE(&_LivepeerToken.CallOpts) +} + +// DOMAINSEPARATOR is a free data retrieval call binding the contract method 0x3644e515. +// +// Solidity: function DOMAIN_SEPARATOR() view returns(bytes32) +func (_LivepeerToken *LivepeerTokenCaller) DOMAINSEPARATOR(opts *bind.CallOpts) ([32]byte, error) { + var out []interface{} + err := _LivepeerToken.contract.Call(opts, &out, "DOMAIN_SEPARATOR") + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// DOMAINSEPARATOR is a free data retrieval call binding the contract method 0x3644e515. +// +// Solidity: function DOMAIN_SEPARATOR() view returns(bytes32) +func (_LivepeerToken *LivepeerTokenSession) DOMAINSEPARATOR() ([32]byte, error) { + return _LivepeerToken.Contract.DOMAINSEPARATOR(&_LivepeerToken.CallOpts) +} + +// DOMAINSEPARATOR is a free data retrieval call binding the contract method 0x3644e515. +// +// Solidity: function DOMAIN_SEPARATOR() view returns(bytes32) +func (_LivepeerToken *LivepeerTokenCallerSession) DOMAINSEPARATOR() ([32]byte, error) { + return _LivepeerToken.Contract.DOMAINSEPARATOR(&_LivepeerToken.CallOpts) +} + // Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. // // Solidity: function allowance(address owner, address spender) view returns(uint256) @@ -272,12 +335,113 @@ func (_LivepeerToken *LivepeerTokenCallerSession) Decimals() (uint8, error) { return _LivepeerToken.Contract.Decimals(&_LivepeerToken.CallOpts) } -// MintingFinished is a free data retrieval call binding the contract method 0x05d2035b. +// Eip712Domain is a free data retrieval call binding the contract method 0x84b0196e. // -// Solidity: function mintingFinished() view returns(bool) -func (_LivepeerToken *LivepeerTokenCaller) MintingFinished(opts *bind.CallOpts) (bool, error) { +// Solidity: function eip712Domain() view returns(bytes1 fields, string name, string version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] extensions) +func (_LivepeerToken *LivepeerTokenCaller) Eip712Domain(opts *bind.CallOpts) (struct { + Fields [1]byte + Name string + Version string + ChainId *big.Int + VerifyingContract common.Address + Salt [32]byte + Extensions []*big.Int +}, error) { var out []interface{} - err := _LivepeerToken.contract.Call(opts, &out, "mintingFinished") + err := _LivepeerToken.contract.Call(opts, &out, "eip712Domain") + + outstruct := new(struct { + Fields [1]byte + Name string + Version string + ChainId *big.Int + VerifyingContract common.Address + Salt [32]byte + Extensions []*big.Int + }) + if err != nil { + return *outstruct, err + } + + outstruct.Fields = *abi.ConvertType(out[0], new([1]byte)).(*[1]byte) + outstruct.Name = *abi.ConvertType(out[1], new(string)).(*string) + outstruct.Version = *abi.ConvertType(out[2], new(string)).(*string) + outstruct.ChainId = *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + outstruct.VerifyingContract = *abi.ConvertType(out[4], new(common.Address)).(*common.Address) + outstruct.Salt = *abi.ConvertType(out[5], new([32]byte)).(*[32]byte) + outstruct.Extensions = *abi.ConvertType(out[6], new([]*big.Int)).(*[]*big.Int) + + return *outstruct, err + +} + +// Eip712Domain is a free data retrieval call binding the contract method 0x84b0196e. +// +// Solidity: function eip712Domain() view returns(bytes1 fields, string name, string version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] extensions) +func (_LivepeerToken *LivepeerTokenSession) Eip712Domain() (struct { + Fields [1]byte + Name string + Version string + ChainId *big.Int + VerifyingContract common.Address + Salt [32]byte + Extensions []*big.Int +}, error) { + return _LivepeerToken.Contract.Eip712Domain(&_LivepeerToken.CallOpts) +} + +// Eip712Domain is a free data retrieval call binding the contract method 0x84b0196e. +// +// Solidity: function eip712Domain() view returns(bytes1 fields, string name, string version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] extensions) +func (_LivepeerToken *LivepeerTokenCallerSession) Eip712Domain() (struct { + Fields [1]byte + Name string + Version string + ChainId *big.Int + VerifyingContract common.Address + Salt [32]byte + Extensions []*big.Int +}, error) { + return _LivepeerToken.Contract.Eip712Domain(&_LivepeerToken.CallOpts) +} + +// GetRoleAdmin is a free data retrieval call binding the contract method 0x248a9ca3. +// +// Solidity: function getRoleAdmin(bytes32 role) view returns(bytes32) +func (_LivepeerToken *LivepeerTokenCaller) GetRoleAdmin(opts *bind.CallOpts, role [32]byte) ([32]byte, error) { + var out []interface{} + err := _LivepeerToken.contract.Call(opts, &out, "getRoleAdmin", role) + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// GetRoleAdmin is a free data retrieval call binding the contract method 0x248a9ca3. +// +// Solidity: function getRoleAdmin(bytes32 role) view returns(bytes32) +func (_LivepeerToken *LivepeerTokenSession) GetRoleAdmin(role [32]byte) ([32]byte, error) { + return _LivepeerToken.Contract.GetRoleAdmin(&_LivepeerToken.CallOpts, role) +} + +// GetRoleAdmin is a free data retrieval call binding the contract method 0x248a9ca3. +// +// Solidity: function getRoleAdmin(bytes32 role) view returns(bytes32) +func (_LivepeerToken *LivepeerTokenCallerSession) GetRoleAdmin(role [32]byte) ([32]byte, error) { + return _LivepeerToken.Contract.GetRoleAdmin(&_LivepeerToken.CallOpts, role) +} + +// HasRole is a free data retrieval call binding the contract method 0x91d14854. +// +// Solidity: function hasRole(bytes32 role, address account) view returns(bool) +func (_LivepeerToken *LivepeerTokenCaller) HasRole(opts *bind.CallOpts, role [32]byte, account common.Address) (bool, error) { + var out []interface{} + err := _LivepeerToken.contract.Call(opts, &out, "hasRole", role, account) if err != nil { return *new(bool), err @@ -289,18 +453,18 @@ func (_LivepeerToken *LivepeerTokenCaller) MintingFinished(opts *bind.CallOpts) } -// MintingFinished is a free data retrieval call binding the contract method 0x05d2035b. +// HasRole is a free data retrieval call binding the contract method 0x91d14854. // -// Solidity: function mintingFinished() view returns(bool) -func (_LivepeerToken *LivepeerTokenSession) MintingFinished() (bool, error) { - return _LivepeerToken.Contract.MintingFinished(&_LivepeerToken.CallOpts) +// Solidity: function hasRole(bytes32 role, address account) view returns(bool) +func (_LivepeerToken *LivepeerTokenSession) HasRole(role [32]byte, account common.Address) (bool, error) { + return _LivepeerToken.Contract.HasRole(&_LivepeerToken.CallOpts, role, account) } -// MintingFinished is a free data retrieval call binding the contract method 0x05d2035b. +// HasRole is a free data retrieval call binding the contract method 0x91d14854. // -// Solidity: function mintingFinished() view returns(bool) -func (_LivepeerToken *LivepeerTokenCallerSession) MintingFinished() (bool, error) { - return _LivepeerToken.Contract.MintingFinished(&_LivepeerToken.CallOpts) +// Solidity: function hasRole(bytes32 role, address account) view returns(bool) +func (_LivepeerToken *LivepeerTokenCallerSession) HasRole(role [32]byte, account common.Address) (bool, error) { + return _LivepeerToken.Contract.HasRole(&_LivepeerToken.CallOpts, role, account) } // Name is a free data retrieval call binding the contract method 0x06fdde03. @@ -334,35 +498,66 @@ func (_LivepeerToken *LivepeerTokenCallerSession) Name() (string, error) { return _LivepeerToken.Contract.Name(&_LivepeerToken.CallOpts) } -// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// Nonces is a free data retrieval call binding the contract method 0x7ecebe00. // -// Solidity: function owner() view returns(address) -func (_LivepeerToken *LivepeerTokenCaller) Owner(opts *bind.CallOpts) (common.Address, error) { +// Solidity: function nonces(address owner) view returns(uint256) +func (_LivepeerToken *LivepeerTokenCaller) Nonces(opts *bind.CallOpts, owner common.Address) (*big.Int, error) { var out []interface{} - err := _LivepeerToken.contract.Call(opts, &out, "owner") + err := _LivepeerToken.contract.Call(opts, &out, "nonces", owner) if err != nil { - return *new(common.Address), err + return *new(*big.Int), err } - out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) return out0, err } -// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// Nonces is a free data retrieval call binding the contract method 0x7ecebe00. // -// Solidity: function owner() view returns(address) -func (_LivepeerToken *LivepeerTokenSession) Owner() (common.Address, error) { - return _LivepeerToken.Contract.Owner(&_LivepeerToken.CallOpts) +// Solidity: function nonces(address owner) view returns(uint256) +func (_LivepeerToken *LivepeerTokenSession) Nonces(owner common.Address) (*big.Int, error) { + return _LivepeerToken.Contract.Nonces(&_LivepeerToken.CallOpts, owner) } -// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// Nonces is a free data retrieval call binding the contract method 0x7ecebe00. // -// Solidity: function owner() view returns(address) -func (_LivepeerToken *LivepeerTokenCallerSession) Owner() (common.Address, error) { - return _LivepeerToken.Contract.Owner(&_LivepeerToken.CallOpts) +// Solidity: function nonces(address owner) view returns(uint256) +func (_LivepeerToken *LivepeerTokenCallerSession) Nonces(owner common.Address) (*big.Int, error) { + return _LivepeerToken.Contract.Nonces(&_LivepeerToken.CallOpts, owner) +} + +// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7. +// +// Solidity: function supportsInterface(bytes4 interfaceId) view returns(bool) +func (_LivepeerToken *LivepeerTokenCaller) SupportsInterface(opts *bind.CallOpts, interfaceId [4]byte) (bool, error) { + var out []interface{} + err := _LivepeerToken.contract.Call(opts, &out, "supportsInterface", interfaceId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7. +// +// Solidity: function supportsInterface(bytes4 interfaceId) view returns(bool) +func (_LivepeerToken *LivepeerTokenSession) SupportsInterface(interfaceId [4]byte) (bool, error) { + return _LivepeerToken.Contract.SupportsInterface(&_LivepeerToken.CallOpts, interfaceId) +} + +// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7. +// +// Solidity: function supportsInterface(bytes4 interfaceId) view returns(bool) +func (_LivepeerToken *LivepeerTokenCallerSession) SupportsInterface(interfaceId [4]byte) (bool, error) { + return _LivepeerToken.Contract.SupportsInterface(&_LivepeerToken.CallOpts, interfaceId) } // Symbol is a free data retrieval call binding the contract method 0x95d89b41. @@ -427,56 +622,25 @@ func (_LivepeerToken *LivepeerTokenCallerSession) TotalSupply() (*big.Int, error return _LivepeerToken.Contract.TotalSupply(&_LivepeerToken.CallOpts) } -// Version is a free data retrieval call binding the contract method 0x54fd4d50. -// -// Solidity: function version() view returns(string) -func (_LivepeerToken *LivepeerTokenCaller) Version(opts *bind.CallOpts) (string, error) { - var out []interface{} - err := _LivepeerToken.contract.Call(opts, &out, "version") - - if err != nil { - return *new(string), err - } - - out0 := *abi.ConvertType(out[0], new(string)).(*string) - - return out0, err - -} - -// Version is a free data retrieval call binding the contract method 0x54fd4d50. -// -// Solidity: function version() view returns(string) -func (_LivepeerToken *LivepeerTokenSession) Version() (string, error) { - return _LivepeerToken.Contract.Version(&_LivepeerToken.CallOpts) -} - -// Version is a free data retrieval call binding the contract method 0x54fd4d50. -// -// Solidity: function version() view returns(string) -func (_LivepeerToken *LivepeerTokenCallerSession) Version() (string, error) { - return _LivepeerToken.Contract.Version(&_LivepeerToken.CallOpts) -} - // Approve is a paid mutator transaction binding the contract method 0x095ea7b3. // -// Solidity: function approve(address spender, uint256 value) returns(bool) -func (_LivepeerToken *LivepeerTokenTransactor) Approve(opts *bind.TransactOpts, spender common.Address, value *big.Int) (*types.Transaction, error) { - return _LivepeerToken.contract.Transact(opts, "approve", spender, value) +// Solidity: function approve(address spender, uint256 amount) returns(bool) +func (_LivepeerToken *LivepeerTokenTransactor) Approve(opts *bind.TransactOpts, spender common.Address, amount *big.Int) (*types.Transaction, error) { + return _LivepeerToken.contract.Transact(opts, "approve", spender, amount) } // Approve is a paid mutator transaction binding the contract method 0x095ea7b3. // -// Solidity: function approve(address spender, uint256 value) returns(bool) -func (_LivepeerToken *LivepeerTokenSession) Approve(spender common.Address, value *big.Int) (*types.Transaction, error) { - return _LivepeerToken.Contract.Approve(&_LivepeerToken.TransactOpts, spender, value) +// Solidity: function approve(address spender, uint256 amount) returns(bool) +func (_LivepeerToken *LivepeerTokenSession) Approve(spender common.Address, amount *big.Int) (*types.Transaction, error) { + return _LivepeerToken.Contract.Approve(&_LivepeerToken.TransactOpts, spender, amount) } // Approve is a paid mutator transaction binding the contract method 0x095ea7b3. // -// Solidity: function approve(address spender, uint256 value) returns(bool) -func (_LivepeerToken *LivepeerTokenTransactorSession) Approve(spender common.Address, value *big.Int) (*types.Transaction, error) { - return _LivepeerToken.Contract.Approve(&_LivepeerToken.TransactOpts, spender, value) +// Solidity: function approve(address spender, uint256 amount) returns(bool) +func (_LivepeerToken *LivepeerTokenTransactorSession) Approve(spender common.Address, amount *big.Int) (*types.Transaction, error) { + return _LivepeerToken.Contract.Approve(&_LivepeerToken.TransactOpts, spender, amount) } // Burn is a paid mutator transaction binding the contract method 0x42966c68. @@ -500,6 +664,27 @@ func (_LivepeerToken *LivepeerTokenTransactorSession) Burn(_amount *big.Int) (*t return _LivepeerToken.Contract.Burn(&_LivepeerToken.TransactOpts, _amount) } +// BurnFrom is a paid mutator transaction binding the contract method 0x79cc6790. +// +// Solidity: function burnFrom(address _from, uint256 _amount) returns() +func (_LivepeerToken *LivepeerTokenTransactor) BurnFrom(opts *bind.TransactOpts, _from common.Address, _amount *big.Int) (*types.Transaction, error) { + return _LivepeerToken.contract.Transact(opts, "burnFrom", _from, _amount) +} + +// BurnFrom is a paid mutator transaction binding the contract method 0x79cc6790. +// +// Solidity: function burnFrom(address _from, uint256 _amount) returns() +func (_LivepeerToken *LivepeerTokenSession) BurnFrom(_from common.Address, _amount *big.Int) (*types.Transaction, error) { + return _LivepeerToken.Contract.BurnFrom(&_LivepeerToken.TransactOpts, _from, _amount) +} + +// BurnFrom is a paid mutator transaction binding the contract method 0x79cc6790. +// +// Solidity: function burnFrom(address _from, uint256 _amount) returns() +func (_LivepeerToken *LivepeerTokenTransactorSession) BurnFrom(_from common.Address, _amount *big.Int) (*types.Transaction, error) { + return _LivepeerToken.Contract.BurnFrom(&_LivepeerToken.TransactOpts, _from, _amount) +} + // DecreaseAllowance is a paid mutator transaction binding the contract method 0xa457c2d7. // // Solidity: function decreaseAllowance(address spender, uint256 subtractedValue) returns(bool) @@ -521,25 +706,25 @@ func (_LivepeerToken *LivepeerTokenTransactorSession) DecreaseAllowance(spender return _LivepeerToken.Contract.DecreaseAllowance(&_LivepeerToken.TransactOpts, spender, subtractedValue) } -// FinishMinting is a paid mutator transaction binding the contract method 0x7d64bcb4. +// GrantRole is a paid mutator transaction binding the contract method 0x2f2ff15d. // -// Solidity: function finishMinting() returns(bool) -func (_LivepeerToken *LivepeerTokenTransactor) FinishMinting(opts *bind.TransactOpts) (*types.Transaction, error) { - return _LivepeerToken.contract.Transact(opts, "finishMinting") +// Solidity: function grantRole(bytes32 role, address account) returns() +func (_LivepeerToken *LivepeerTokenTransactor) GrantRole(opts *bind.TransactOpts, role [32]byte, account common.Address) (*types.Transaction, error) { + return _LivepeerToken.contract.Transact(opts, "grantRole", role, account) } -// FinishMinting is a paid mutator transaction binding the contract method 0x7d64bcb4. +// GrantRole is a paid mutator transaction binding the contract method 0x2f2ff15d. // -// Solidity: function finishMinting() returns(bool) -func (_LivepeerToken *LivepeerTokenSession) FinishMinting() (*types.Transaction, error) { - return _LivepeerToken.Contract.FinishMinting(&_LivepeerToken.TransactOpts) +// Solidity: function grantRole(bytes32 role, address account) returns() +func (_LivepeerToken *LivepeerTokenSession) GrantRole(role [32]byte, account common.Address) (*types.Transaction, error) { + return _LivepeerToken.Contract.GrantRole(&_LivepeerToken.TransactOpts, role, account) } -// FinishMinting is a paid mutator transaction binding the contract method 0x7d64bcb4. +// GrantRole is a paid mutator transaction binding the contract method 0x2f2ff15d. // -// Solidity: function finishMinting() returns(bool) -func (_LivepeerToken *LivepeerTokenTransactorSession) FinishMinting() (*types.Transaction, error) { - return _LivepeerToken.Contract.FinishMinting(&_LivepeerToken.TransactOpts) +// Solidity: function grantRole(bytes32 role, address account) returns() +func (_LivepeerToken *LivepeerTokenTransactorSession) GrantRole(role [32]byte, account common.Address) (*types.Transaction, error) { + return _LivepeerToken.Contract.GrantRole(&_LivepeerToken.TransactOpts, role, account) } // IncreaseAllowance is a paid mutator transaction binding the contract method 0x39509351. @@ -565,86 +750,128 @@ func (_LivepeerToken *LivepeerTokenTransactorSession) IncreaseAllowance(spender // Mint is a paid mutator transaction binding the contract method 0x40c10f19. // -// Solidity: function mint(address _to, uint256 _amount) returns(bool) +// Solidity: function mint(address _to, uint256 _amount) returns() func (_LivepeerToken *LivepeerTokenTransactor) Mint(opts *bind.TransactOpts, _to common.Address, _amount *big.Int) (*types.Transaction, error) { return _LivepeerToken.contract.Transact(opts, "mint", _to, _amount) } // Mint is a paid mutator transaction binding the contract method 0x40c10f19. // -// Solidity: function mint(address _to, uint256 _amount) returns(bool) +// Solidity: function mint(address _to, uint256 _amount) returns() func (_LivepeerToken *LivepeerTokenSession) Mint(_to common.Address, _amount *big.Int) (*types.Transaction, error) { return _LivepeerToken.Contract.Mint(&_LivepeerToken.TransactOpts, _to, _amount) } // Mint is a paid mutator transaction binding the contract method 0x40c10f19. // -// Solidity: function mint(address _to, uint256 _amount) returns(bool) +// Solidity: function mint(address _to, uint256 _amount) returns() func (_LivepeerToken *LivepeerTokenTransactorSession) Mint(_to common.Address, _amount *big.Int) (*types.Transaction, error) { return _LivepeerToken.Contract.Mint(&_LivepeerToken.TransactOpts, _to, _amount) } -// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// Permit is a paid mutator transaction binding the contract method 0xd505accf. // -// Solidity: function transfer(address recipient, uint256 amount) returns(bool) -func (_LivepeerToken *LivepeerTokenTransactor) Transfer(opts *bind.TransactOpts, recipient common.Address, amount *big.Int) (*types.Transaction, error) { - return _LivepeerToken.contract.Transact(opts, "transfer", recipient, amount) +// Solidity: function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) returns() +func (_LivepeerToken *LivepeerTokenTransactor) Permit(opts *bind.TransactOpts, owner common.Address, spender common.Address, value *big.Int, deadline *big.Int, v uint8, r [32]byte, s [32]byte) (*types.Transaction, error) { + return _LivepeerToken.contract.Transact(opts, "permit", owner, spender, value, deadline, v, r, s) } -// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// Permit is a paid mutator transaction binding the contract method 0xd505accf. // -// Solidity: function transfer(address recipient, uint256 amount) returns(bool) -func (_LivepeerToken *LivepeerTokenSession) Transfer(recipient common.Address, amount *big.Int) (*types.Transaction, error) { - return _LivepeerToken.Contract.Transfer(&_LivepeerToken.TransactOpts, recipient, amount) +// Solidity: function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) returns() +func (_LivepeerToken *LivepeerTokenSession) Permit(owner common.Address, spender common.Address, value *big.Int, deadline *big.Int, v uint8, r [32]byte, s [32]byte) (*types.Transaction, error) { + return _LivepeerToken.Contract.Permit(&_LivepeerToken.TransactOpts, owner, spender, value, deadline, v, r, s) } -// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// Permit is a paid mutator transaction binding the contract method 0xd505accf. // -// Solidity: function transfer(address recipient, uint256 amount) returns(bool) -func (_LivepeerToken *LivepeerTokenTransactorSession) Transfer(recipient common.Address, amount *big.Int) (*types.Transaction, error) { - return _LivepeerToken.Contract.Transfer(&_LivepeerToken.TransactOpts, recipient, amount) +// Solidity: function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) returns() +func (_LivepeerToken *LivepeerTokenTransactorSession) Permit(owner common.Address, spender common.Address, value *big.Int, deadline *big.Int, v uint8, r [32]byte, s [32]byte) (*types.Transaction, error) { + return _LivepeerToken.Contract.Permit(&_LivepeerToken.TransactOpts, owner, spender, value, deadline, v, r, s) } -// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// RenounceRole is a paid mutator transaction binding the contract method 0x36568abe. // -// Solidity: function transferFrom(address sender, address recipient, uint256 amount) returns(bool) -func (_LivepeerToken *LivepeerTokenTransactor) TransferFrom(opts *bind.TransactOpts, sender common.Address, recipient common.Address, amount *big.Int) (*types.Transaction, error) { - return _LivepeerToken.contract.Transact(opts, "transferFrom", sender, recipient, amount) +// Solidity: function renounceRole(bytes32 role, address account) returns() +func (_LivepeerToken *LivepeerTokenTransactor) RenounceRole(opts *bind.TransactOpts, role [32]byte, account common.Address) (*types.Transaction, error) { + return _LivepeerToken.contract.Transact(opts, "renounceRole", role, account) } -// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// RenounceRole is a paid mutator transaction binding the contract method 0x36568abe. // -// Solidity: function transferFrom(address sender, address recipient, uint256 amount) returns(bool) -func (_LivepeerToken *LivepeerTokenSession) TransferFrom(sender common.Address, recipient common.Address, amount *big.Int) (*types.Transaction, error) { - return _LivepeerToken.Contract.TransferFrom(&_LivepeerToken.TransactOpts, sender, recipient, amount) +// Solidity: function renounceRole(bytes32 role, address account) returns() +func (_LivepeerToken *LivepeerTokenSession) RenounceRole(role [32]byte, account common.Address) (*types.Transaction, error) { + return _LivepeerToken.Contract.RenounceRole(&_LivepeerToken.TransactOpts, role, account) } -// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// RenounceRole is a paid mutator transaction binding the contract method 0x36568abe. +// +// Solidity: function renounceRole(bytes32 role, address account) returns() +func (_LivepeerToken *LivepeerTokenTransactorSession) RenounceRole(role [32]byte, account common.Address) (*types.Transaction, error) { + return _LivepeerToken.Contract.RenounceRole(&_LivepeerToken.TransactOpts, role, account) +} + +// RevokeRole is a paid mutator transaction binding the contract method 0xd547741f. +// +// Solidity: function revokeRole(bytes32 role, address account) returns() +func (_LivepeerToken *LivepeerTokenTransactor) RevokeRole(opts *bind.TransactOpts, role [32]byte, account common.Address) (*types.Transaction, error) { + return _LivepeerToken.contract.Transact(opts, "revokeRole", role, account) +} + +// RevokeRole is a paid mutator transaction binding the contract method 0xd547741f. +// +// Solidity: function revokeRole(bytes32 role, address account) returns() +func (_LivepeerToken *LivepeerTokenSession) RevokeRole(role [32]byte, account common.Address) (*types.Transaction, error) { + return _LivepeerToken.Contract.RevokeRole(&_LivepeerToken.TransactOpts, role, account) +} + +// RevokeRole is a paid mutator transaction binding the contract method 0xd547741f. +// +// Solidity: function revokeRole(bytes32 role, address account) returns() +func (_LivepeerToken *LivepeerTokenTransactorSession) RevokeRole(role [32]byte, account common.Address) (*types.Transaction, error) { + return _LivepeerToken.Contract.RevokeRole(&_LivepeerToken.TransactOpts, role, account) +} + +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// +// Solidity: function transfer(address to, uint256 amount) returns(bool) +func (_LivepeerToken *LivepeerTokenTransactor) Transfer(opts *bind.TransactOpts, to common.Address, amount *big.Int) (*types.Transaction, error) { + return _LivepeerToken.contract.Transact(opts, "transfer", to, amount) +} + +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. // -// Solidity: function transferFrom(address sender, address recipient, uint256 amount) returns(bool) -func (_LivepeerToken *LivepeerTokenTransactorSession) TransferFrom(sender common.Address, recipient common.Address, amount *big.Int) (*types.Transaction, error) { - return _LivepeerToken.Contract.TransferFrom(&_LivepeerToken.TransactOpts, sender, recipient, amount) +// Solidity: function transfer(address to, uint256 amount) returns(bool) +func (_LivepeerToken *LivepeerTokenSession) Transfer(to common.Address, amount *big.Int) (*types.Transaction, error) { + return _LivepeerToken.Contract.Transfer(&_LivepeerToken.TransactOpts, to, amount) } -// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. // -// Solidity: function transferOwnership(address newOwner) returns() -func (_LivepeerToken *LivepeerTokenTransactor) TransferOwnership(opts *bind.TransactOpts, newOwner common.Address) (*types.Transaction, error) { - return _LivepeerToken.contract.Transact(opts, "transferOwnership", newOwner) +// Solidity: function transfer(address to, uint256 amount) returns(bool) +func (_LivepeerToken *LivepeerTokenTransactorSession) Transfer(to common.Address, amount *big.Int) (*types.Transaction, error) { + return _LivepeerToken.Contract.Transfer(&_LivepeerToken.TransactOpts, to, amount) } -// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. // -// Solidity: function transferOwnership(address newOwner) returns() -func (_LivepeerToken *LivepeerTokenSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { - return _LivepeerToken.Contract.TransferOwnership(&_LivepeerToken.TransactOpts, newOwner) +// Solidity: function transferFrom(address from, address to, uint256 amount) returns(bool) +func (_LivepeerToken *LivepeerTokenTransactor) TransferFrom(opts *bind.TransactOpts, from common.Address, to common.Address, amount *big.Int) (*types.Transaction, error) { + return _LivepeerToken.contract.Transact(opts, "transferFrom", from, to, amount) } -// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. // -// Solidity: function transferOwnership(address newOwner) returns() -func (_LivepeerToken *LivepeerTokenTransactorSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { - return _LivepeerToken.Contract.TransferOwnership(&_LivepeerToken.TransactOpts, newOwner) +// Solidity: function transferFrom(address from, address to, uint256 amount) returns(bool) +func (_LivepeerToken *LivepeerTokenSession) TransferFrom(from common.Address, to common.Address, amount *big.Int) (*types.Transaction, error) { + return _LivepeerToken.Contract.TransferFrom(&_LivepeerToken.TransactOpts, from, to, amount) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address from, address to, uint256 amount) returns(bool) +func (_LivepeerToken *LivepeerTokenTransactorSession) TransferFrom(from common.Address, to common.Address, amount *big.Int) (*types.Transaction, error) { + return _LivepeerToken.Contract.TransferFrom(&_LivepeerToken.TransactOpts, from, to, amount) } // LivepeerTokenApprovalIterator is returned from FilterApproval and is used to iterate over the raw logs and unpacked data for Approval events raised by the LivepeerToken contract. @@ -871,13 +1098,13 @@ func (it *LivepeerTokenBurnIterator) Close() error { // LivepeerTokenBurn represents a Burn event raised by the LivepeerToken contract. type LivepeerTokenBurn struct { Burner common.Address - Value *big.Int + Amount *big.Int Raw types.Log // Blockchain specific contextual infos } // FilterBurn is a free log retrieval operation binding the contract event 0xcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5. // -// Solidity: event Burn(address indexed burner, uint256 value) +// Solidity: event Burn(address indexed burner, uint256 amount) func (_LivepeerToken *LivepeerTokenFilterer) FilterBurn(opts *bind.FilterOpts, burner []common.Address) (*LivepeerTokenBurnIterator, error) { var burnerRule []interface{} @@ -894,7 +1121,7 @@ func (_LivepeerToken *LivepeerTokenFilterer) FilterBurn(opts *bind.FilterOpts, b // WatchBurn is a free log subscription operation binding the contract event 0xcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5. // -// Solidity: event Burn(address indexed burner, uint256 value) +// Solidity: event Burn(address indexed burner, uint256 amount) func (_LivepeerToken *LivepeerTokenFilterer) WatchBurn(opts *bind.WatchOpts, sink chan<- *LivepeerTokenBurn, burner []common.Address) (event.Subscription, error) { var burnerRule []interface{} @@ -936,7 +1163,7 @@ func (_LivepeerToken *LivepeerTokenFilterer) WatchBurn(opts *bind.WatchOpts, sin // ParseBurn is a log parse operation binding the contract event 0xcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5. // -// Solidity: event Burn(address indexed burner, uint256 value) +// Solidity: event Burn(address indexed burner, uint256 amount) func (_LivepeerToken *LivepeerTokenFilterer) ParseBurn(log types.Log) (*LivepeerTokenBurn, error) { event := new(LivepeerTokenBurn) if err := _LivepeerToken.contract.UnpackLog(event, "Burn", log); err != nil { @@ -946,6 +1173,139 @@ func (_LivepeerToken *LivepeerTokenFilterer) ParseBurn(log types.Log) (*Livepeer return event, nil } +// LivepeerTokenEIP712DomainChangedIterator is returned from FilterEIP712DomainChanged and is used to iterate over the raw logs and unpacked data for EIP712DomainChanged events raised by the LivepeerToken contract. +type LivepeerTokenEIP712DomainChangedIterator struct { + Event *LivepeerTokenEIP712DomainChanged // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *LivepeerTokenEIP712DomainChangedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(LivepeerTokenEIP712DomainChanged) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(LivepeerTokenEIP712DomainChanged) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *LivepeerTokenEIP712DomainChangedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *LivepeerTokenEIP712DomainChangedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// LivepeerTokenEIP712DomainChanged represents a EIP712DomainChanged event raised by the LivepeerToken contract. +type LivepeerTokenEIP712DomainChanged struct { + Raw types.Log // Blockchain specific contextual infos +} + +// FilterEIP712DomainChanged is a free log retrieval operation binding the contract event 0x0a6387c9ea3628b88a633bb4f3b151770f70085117a15f9bf3787cda53f13d31. +// +// Solidity: event EIP712DomainChanged() +func (_LivepeerToken *LivepeerTokenFilterer) FilterEIP712DomainChanged(opts *bind.FilterOpts) (*LivepeerTokenEIP712DomainChangedIterator, error) { + + logs, sub, err := _LivepeerToken.contract.FilterLogs(opts, "EIP712DomainChanged") + if err != nil { + return nil, err + } + return &LivepeerTokenEIP712DomainChangedIterator{contract: _LivepeerToken.contract, event: "EIP712DomainChanged", logs: logs, sub: sub}, nil +} + +// WatchEIP712DomainChanged is a free log subscription operation binding the contract event 0x0a6387c9ea3628b88a633bb4f3b151770f70085117a15f9bf3787cda53f13d31. +// +// Solidity: event EIP712DomainChanged() +func (_LivepeerToken *LivepeerTokenFilterer) WatchEIP712DomainChanged(opts *bind.WatchOpts, sink chan<- *LivepeerTokenEIP712DomainChanged) (event.Subscription, error) { + + logs, sub, err := _LivepeerToken.contract.WatchLogs(opts, "EIP712DomainChanged") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(LivepeerTokenEIP712DomainChanged) + if err := _LivepeerToken.contract.UnpackLog(event, "EIP712DomainChanged", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseEIP712DomainChanged is a log parse operation binding the contract event 0x0a6387c9ea3628b88a633bb4f3b151770f70085117a15f9bf3787cda53f13d31. +// +// Solidity: event EIP712DomainChanged() +func (_LivepeerToken *LivepeerTokenFilterer) ParseEIP712DomainChanged(log types.Log) (*LivepeerTokenEIP712DomainChanged, error) { + event := new(LivepeerTokenEIP712DomainChanged) + if err := _LivepeerToken.contract.UnpackLog(event, "EIP712DomainChanged", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + // LivepeerTokenMintIterator is returned from FilterMint and is used to iterate over the raw logs and unpacked data for Mint events raised by the LivepeerToken contract. type LivepeerTokenMintIterator struct { Event *LivepeerTokenMint // Event containing the contract specifics and raw log @@ -1091,9 +1451,9 @@ func (_LivepeerToken *LivepeerTokenFilterer) ParseMint(log types.Log) (*Livepeer return event, nil } -// LivepeerTokenMintFinishedIterator is returned from FilterMintFinished and is used to iterate over the raw logs and unpacked data for MintFinished events raised by the LivepeerToken contract. -type LivepeerTokenMintFinishedIterator struct { - Event *LivepeerTokenMintFinished // Event containing the contract specifics and raw log +// LivepeerTokenRoleAdminChangedIterator is returned from FilterRoleAdminChanged and is used to iterate over the raw logs and unpacked data for RoleAdminChanged events raised by the LivepeerToken contract. +type LivepeerTokenRoleAdminChangedIterator struct { + Event *LivepeerTokenRoleAdminChanged // Event containing the contract specifics and raw log contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data @@ -1107,7 +1467,7 @@ type LivepeerTokenMintFinishedIterator struct { // Next advances the iterator to the subsequent event, returning whether there // are any more events found. In case of a retrieval or parsing error, false is // returned and Error() can be queried for the exact failure. -func (it *LivepeerTokenMintFinishedIterator) Next() bool { +func (it *LivepeerTokenRoleAdminChangedIterator) Next() bool { // If the iterator failed, stop iterating if it.fail != nil { return false @@ -1116,7 +1476,7 @@ func (it *LivepeerTokenMintFinishedIterator) Next() bool { if it.done { select { case log := <-it.logs: - it.Event = new(LivepeerTokenMintFinished) + it.Event = new(LivepeerTokenRoleAdminChanged) if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { it.fail = err return false @@ -1131,7 +1491,7 @@ func (it *LivepeerTokenMintFinishedIterator) Next() bool { // Iterator still in progress, wait for either a data or an error event select { case log := <-it.logs: - it.Event = new(LivepeerTokenMintFinished) + it.Event = new(LivepeerTokenRoleAdminChanged) if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { it.fail = err return false @@ -1147,40 +1507,231 @@ func (it *LivepeerTokenMintFinishedIterator) Next() bool { } // Error returns any retrieval or parsing error occurred during filtering. -func (it *LivepeerTokenMintFinishedIterator) Error() error { +func (it *LivepeerTokenRoleAdminChangedIterator) Error() error { return it.fail } // Close terminates the iteration process, releasing any pending underlying // resources. -func (it *LivepeerTokenMintFinishedIterator) Close() error { +func (it *LivepeerTokenRoleAdminChangedIterator) Close() error { it.sub.Unsubscribe() return nil } -// LivepeerTokenMintFinished represents a MintFinished event raised by the LivepeerToken contract. -type LivepeerTokenMintFinished struct { - Raw types.Log // Blockchain specific contextual infos +// LivepeerTokenRoleAdminChanged represents a RoleAdminChanged event raised by the LivepeerToken contract. +type LivepeerTokenRoleAdminChanged struct { + Role [32]byte + PreviousAdminRole [32]byte + NewAdminRole [32]byte + Raw types.Log // Blockchain specific contextual infos +} + +// FilterRoleAdminChanged is a free log retrieval operation binding the contract event 0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff. +// +// Solidity: event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole) +func (_LivepeerToken *LivepeerTokenFilterer) FilterRoleAdminChanged(opts *bind.FilterOpts, role [][32]byte, previousAdminRole [][32]byte, newAdminRole [][32]byte) (*LivepeerTokenRoleAdminChangedIterator, error) { + + var roleRule []interface{} + for _, roleItem := range role { + roleRule = append(roleRule, roleItem) + } + var previousAdminRoleRule []interface{} + for _, previousAdminRoleItem := range previousAdminRole { + previousAdminRoleRule = append(previousAdminRoleRule, previousAdminRoleItem) + } + var newAdminRoleRule []interface{} + for _, newAdminRoleItem := range newAdminRole { + newAdminRoleRule = append(newAdminRoleRule, newAdminRoleItem) + } + + logs, sub, err := _LivepeerToken.contract.FilterLogs(opts, "RoleAdminChanged", roleRule, previousAdminRoleRule, newAdminRoleRule) + if err != nil { + return nil, err + } + return &LivepeerTokenRoleAdminChangedIterator{contract: _LivepeerToken.contract, event: "RoleAdminChanged", logs: logs, sub: sub}, nil +} + +// WatchRoleAdminChanged is a free log subscription operation binding the contract event 0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff. +// +// Solidity: event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole) +func (_LivepeerToken *LivepeerTokenFilterer) WatchRoleAdminChanged(opts *bind.WatchOpts, sink chan<- *LivepeerTokenRoleAdminChanged, role [][32]byte, previousAdminRole [][32]byte, newAdminRole [][32]byte) (event.Subscription, error) { + + var roleRule []interface{} + for _, roleItem := range role { + roleRule = append(roleRule, roleItem) + } + var previousAdminRoleRule []interface{} + for _, previousAdminRoleItem := range previousAdminRole { + previousAdminRoleRule = append(previousAdminRoleRule, previousAdminRoleItem) + } + var newAdminRoleRule []interface{} + for _, newAdminRoleItem := range newAdminRole { + newAdminRoleRule = append(newAdminRoleRule, newAdminRoleItem) + } + + logs, sub, err := _LivepeerToken.contract.WatchLogs(opts, "RoleAdminChanged", roleRule, previousAdminRoleRule, newAdminRoleRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(LivepeerTokenRoleAdminChanged) + if err := _LivepeerToken.contract.UnpackLog(event, "RoleAdminChanged", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil } -// FilterMintFinished is a free log retrieval operation binding the contract event 0xae5184fba832cb2b1f702aca6117b8d265eaf03ad33eb133f19dde0f5920fa08. +// ParseRoleAdminChanged is a log parse operation binding the contract event 0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff. // -// Solidity: event MintFinished() -func (_LivepeerToken *LivepeerTokenFilterer) FilterMintFinished(opts *bind.FilterOpts) (*LivepeerTokenMintFinishedIterator, error) { +// Solidity: event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole) +func (_LivepeerToken *LivepeerTokenFilterer) ParseRoleAdminChanged(log types.Log) (*LivepeerTokenRoleAdminChanged, error) { + event := new(LivepeerTokenRoleAdminChanged) + if err := _LivepeerToken.contract.UnpackLog(event, "RoleAdminChanged", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// LivepeerTokenRoleGrantedIterator is returned from FilterRoleGranted and is used to iterate over the raw logs and unpacked data for RoleGranted events raised by the LivepeerToken contract. +type LivepeerTokenRoleGrantedIterator struct { + Event *LivepeerTokenRoleGranted // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *LivepeerTokenRoleGrantedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(LivepeerTokenRoleGranted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true - logs, sub, err := _LivepeerToken.contract.FilterLogs(opts, "MintFinished") + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(LivepeerTokenRoleGranted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *LivepeerTokenRoleGrantedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *LivepeerTokenRoleGrantedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// LivepeerTokenRoleGranted represents a RoleGranted event raised by the LivepeerToken contract. +type LivepeerTokenRoleGranted struct { + Role [32]byte + Account common.Address + Sender common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterRoleGranted is a free log retrieval operation binding the contract event 0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d. +// +// Solidity: event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender) +func (_LivepeerToken *LivepeerTokenFilterer) FilterRoleGranted(opts *bind.FilterOpts, role [][32]byte, account []common.Address, sender []common.Address) (*LivepeerTokenRoleGrantedIterator, error) { + + var roleRule []interface{} + for _, roleItem := range role { + roleRule = append(roleRule, roleItem) + } + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _LivepeerToken.contract.FilterLogs(opts, "RoleGranted", roleRule, accountRule, senderRule) if err != nil { return nil, err } - return &LivepeerTokenMintFinishedIterator{contract: _LivepeerToken.contract, event: "MintFinished", logs: logs, sub: sub}, nil + return &LivepeerTokenRoleGrantedIterator{contract: _LivepeerToken.contract, event: "RoleGranted", logs: logs, sub: sub}, nil } -// WatchMintFinished is a free log subscription operation binding the contract event 0xae5184fba832cb2b1f702aca6117b8d265eaf03ad33eb133f19dde0f5920fa08. +// WatchRoleGranted is a free log subscription operation binding the contract event 0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d. // -// Solidity: event MintFinished() -func (_LivepeerToken *LivepeerTokenFilterer) WatchMintFinished(opts *bind.WatchOpts, sink chan<- *LivepeerTokenMintFinished) (event.Subscription, error) { +// Solidity: event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender) +func (_LivepeerToken *LivepeerTokenFilterer) WatchRoleGranted(opts *bind.WatchOpts, sink chan<- *LivepeerTokenRoleGranted, role [][32]byte, account []common.Address, sender []common.Address) (event.Subscription, error) { - logs, sub, err := _LivepeerToken.contract.WatchLogs(opts, "MintFinished") + var roleRule []interface{} + for _, roleItem := range role { + roleRule = append(roleRule, roleItem) + } + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _LivepeerToken.contract.WatchLogs(opts, "RoleGranted", roleRule, accountRule, senderRule) if err != nil { return nil, err } @@ -1190,8 +1741,8 @@ func (_LivepeerToken *LivepeerTokenFilterer) WatchMintFinished(opts *bind.WatchO select { case log := <-logs: // New log arrived, parse the event and forward to the user - event := new(LivepeerTokenMintFinished) - if err := _LivepeerToken.contract.UnpackLog(event, "MintFinished", log); err != nil { + event := new(LivepeerTokenRoleGranted) + if err := _LivepeerToken.contract.UnpackLog(event, "RoleGranted", log); err != nil { return err } event.Raw = log @@ -1212,21 +1763,21 @@ func (_LivepeerToken *LivepeerTokenFilterer) WatchMintFinished(opts *bind.WatchO }), nil } -// ParseMintFinished is a log parse operation binding the contract event 0xae5184fba832cb2b1f702aca6117b8d265eaf03ad33eb133f19dde0f5920fa08. +// ParseRoleGranted is a log parse operation binding the contract event 0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d. // -// Solidity: event MintFinished() -func (_LivepeerToken *LivepeerTokenFilterer) ParseMintFinished(log types.Log) (*LivepeerTokenMintFinished, error) { - event := new(LivepeerTokenMintFinished) - if err := _LivepeerToken.contract.UnpackLog(event, "MintFinished", log); err != nil { +// Solidity: event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender) +func (_LivepeerToken *LivepeerTokenFilterer) ParseRoleGranted(log types.Log) (*LivepeerTokenRoleGranted, error) { + event := new(LivepeerTokenRoleGranted) + if err := _LivepeerToken.contract.UnpackLog(event, "RoleGranted", log); err != nil { return nil, err } event.Raw = log return event, nil } -// LivepeerTokenOwnershipTransferredIterator is returned from FilterOwnershipTransferred and is used to iterate over the raw logs and unpacked data for OwnershipTransferred events raised by the LivepeerToken contract. -type LivepeerTokenOwnershipTransferredIterator struct { - Event *LivepeerTokenOwnershipTransferred // Event containing the contract specifics and raw log +// LivepeerTokenRoleRevokedIterator is returned from FilterRoleRevoked and is used to iterate over the raw logs and unpacked data for RoleRevoked events raised by the LivepeerToken contract. +type LivepeerTokenRoleRevokedIterator struct { + Event *LivepeerTokenRoleRevoked // Event containing the contract specifics and raw log contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data @@ -1240,7 +1791,7 @@ type LivepeerTokenOwnershipTransferredIterator struct { // Next advances the iterator to the subsequent event, returning whether there // are any more events found. In case of a retrieval or parsing error, false is // returned and Error() can be queried for the exact failure. -func (it *LivepeerTokenOwnershipTransferredIterator) Next() bool { +func (it *LivepeerTokenRoleRevokedIterator) Next() bool { // If the iterator failed, stop iterating if it.fail != nil { return false @@ -1249,7 +1800,7 @@ func (it *LivepeerTokenOwnershipTransferredIterator) Next() bool { if it.done { select { case log := <-it.logs: - it.Event = new(LivepeerTokenOwnershipTransferred) + it.Event = new(LivepeerTokenRoleRevoked) if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { it.fail = err return false @@ -1264,7 +1815,7 @@ func (it *LivepeerTokenOwnershipTransferredIterator) Next() bool { // Iterator still in progress, wait for either a data or an error event select { case log := <-it.logs: - it.Event = new(LivepeerTokenOwnershipTransferred) + it.Event = new(LivepeerTokenRoleRevoked) if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { it.fail = err return false @@ -1280,60 +1831,69 @@ func (it *LivepeerTokenOwnershipTransferredIterator) Next() bool { } // Error returns any retrieval or parsing error occurred during filtering. -func (it *LivepeerTokenOwnershipTransferredIterator) Error() error { +func (it *LivepeerTokenRoleRevokedIterator) Error() error { return it.fail } // Close terminates the iteration process, releasing any pending underlying // resources. -func (it *LivepeerTokenOwnershipTransferredIterator) Close() error { +func (it *LivepeerTokenRoleRevokedIterator) Close() error { it.sub.Unsubscribe() return nil } -// LivepeerTokenOwnershipTransferred represents a OwnershipTransferred event raised by the LivepeerToken contract. -type LivepeerTokenOwnershipTransferred struct { - PreviousOwner common.Address - NewOwner common.Address - Raw types.Log // Blockchain specific contextual infos +// LivepeerTokenRoleRevoked represents a RoleRevoked event raised by the LivepeerToken contract. +type LivepeerTokenRoleRevoked struct { + Role [32]byte + Account common.Address + Sender common.Address + Raw types.Log // Blockchain specific contextual infos } -// FilterOwnershipTransferred is a free log retrieval operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// FilterRoleRevoked is a free log retrieval operation binding the contract event 0xf6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b. // -// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) -func (_LivepeerToken *LivepeerTokenFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, previousOwner []common.Address, newOwner []common.Address) (*LivepeerTokenOwnershipTransferredIterator, error) { +// Solidity: event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender) +func (_LivepeerToken *LivepeerTokenFilterer) FilterRoleRevoked(opts *bind.FilterOpts, role [][32]byte, account []common.Address, sender []common.Address) (*LivepeerTokenRoleRevokedIterator, error) { - var previousOwnerRule []interface{} - for _, previousOwnerItem := range previousOwner { - previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + var roleRule []interface{} + for _, roleItem := range role { + roleRule = append(roleRule, roleItem) } - var newOwnerRule []interface{} - for _, newOwnerItem := range newOwner { - newOwnerRule = append(newOwnerRule, newOwnerItem) + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) } - logs, sub, err := _LivepeerToken.contract.FilterLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule) + logs, sub, err := _LivepeerToken.contract.FilterLogs(opts, "RoleRevoked", roleRule, accountRule, senderRule) if err != nil { return nil, err } - return &LivepeerTokenOwnershipTransferredIterator{contract: _LivepeerToken.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil + return &LivepeerTokenRoleRevokedIterator{contract: _LivepeerToken.contract, event: "RoleRevoked", logs: logs, sub: sub}, nil } -// WatchOwnershipTransferred is a free log subscription operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// WatchRoleRevoked is a free log subscription operation binding the contract event 0xf6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b. // -// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) -func (_LivepeerToken *LivepeerTokenFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *LivepeerTokenOwnershipTransferred, previousOwner []common.Address, newOwner []common.Address) (event.Subscription, error) { +// Solidity: event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender) +func (_LivepeerToken *LivepeerTokenFilterer) WatchRoleRevoked(opts *bind.WatchOpts, sink chan<- *LivepeerTokenRoleRevoked, role [][32]byte, account []common.Address, sender []common.Address) (event.Subscription, error) { - var previousOwnerRule []interface{} - for _, previousOwnerItem := range previousOwner { - previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + var roleRule []interface{} + for _, roleItem := range role { + roleRule = append(roleRule, roleItem) + } + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) } - var newOwnerRule []interface{} - for _, newOwnerItem := range newOwner { - newOwnerRule = append(newOwnerRule, newOwnerItem) + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) } - logs, sub, err := _LivepeerToken.contract.WatchLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule) + logs, sub, err := _LivepeerToken.contract.WatchLogs(opts, "RoleRevoked", roleRule, accountRule, senderRule) if err != nil { return nil, err } @@ -1343,8 +1903,8 @@ func (_LivepeerToken *LivepeerTokenFilterer) WatchOwnershipTransferred(opts *bin select { case log := <-logs: // New log arrived, parse the event and forward to the user - event := new(LivepeerTokenOwnershipTransferred) - if err := _LivepeerToken.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + event := new(LivepeerTokenRoleRevoked) + if err := _LivepeerToken.contract.UnpackLog(event, "RoleRevoked", log); err != nil { return err } event.Raw = log @@ -1365,12 +1925,12 @@ func (_LivepeerToken *LivepeerTokenFilterer) WatchOwnershipTransferred(opts *bin }), nil } -// ParseOwnershipTransferred is a log parse operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// ParseRoleRevoked is a log parse operation binding the contract event 0xf6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b. // -// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) -func (_LivepeerToken *LivepeerTokenFilterer) ParseOwnershipTransferred(log types.Log) (*LivepeerTokenOwnershipTransferred, error) { - event := new(LivepeerTokenOwnershipTransferred) - if err := _LivepeerToken.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { +// Solidity: event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender) +func (_LivepeerToken *LivepeerTokenFilterer) ParseRoleRevoked(log types.Log) (*LivepeerTokenRoleRevoked, error) { + event := new(LivepeerTokenRoleRevoked) + if err := _LivepeerToken.contract.UnpackLog(event, "RoleRevoked", log); err != nil { return nil, err } event.Raw = log diff --git a/eth/contracts/livepeerTokenFaucet.go b/eth/contracts/livepeerTokenFaucet.go index 53f1923a04..055bbb6cb0 100644 --- a/eth/contracts/livepeerTokenFaucet.go +++ b/eth/contracts/livepeerTokenFaucet.go @@ -26,11 +26,12 @@ var ( _ = common.Big1 _ = types.BloomLookup _ = event.NewSubscription + _ = abi.ConvertType ) // LivepeerTokenFaucetMetaData contains all meta data concerning the LivepeerTokenFaucet contract. var LivepeerTokenFaucetMetaData = &bind.MetaData{ - ABI: "[{\"constant\":true,\"inputs\":[],\"name\":\"requestWait\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"nextValidRequest\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"request\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"isWhitelisted\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"removeFromWhitelist\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"addToWhitelist\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"requestAmount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"token\",\"outputs\":[{\"internalType\":\"contractILivepeerToken\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_requestAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_requestWait\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Request\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"}]", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_requestAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_requestWait\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Request\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"addToWhitelist\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"isWhitelisted\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"nextValidRequest\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"removeFromWhitelist\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"request\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"requestAmount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"requestWait\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"token\",\"outputs\":[{\"internalType\":\"contractILivepeerToken\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", } // LivepeerTokenFaucetABI is the input ABI used to generate the binding from. @@ -134,11 +135,11 @@ func NewLivepeerTokenFaucetFilterer(address common.Address, filterer bind.Contra // bindLivepeerTokenFaucet binds a generic wrapper to an already deployed contract. func bindLivepeerTokenFaucet(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { - parsed, err := abi.JSON(strings.NewReader(LivepeerTokenFaucetABI)) + parsed, err := LivepeerTokenFaucetMetaData.GetAbi() if err != nil { return nil, err } - return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil } // Call invokes the (constant) contract method with params as input values and diff --git a/eth/contracts/minter.go b/eth/contracts/minter.go index 40e047ccfd..0f1937c3d8 100644 --- a/eth/contracts/minter.go +++ b/eth/contracts/minter.go @@ -26,11 +26,12 @@ var ( _ = common.Big1 _ = types.BloomLookup _ = event.NewSubscription + _ = abi.ConvertType ) // MinterMetaData contains all meta data concerning the Minter contract. var MinterMetaData = &bind.MetaData{ - ABI: "[{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_inflationChange\",\"type\":\"uint256\"}],\"name\":\"setInflationChange\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"contractIMinter\",\"name\":\"_newMinter\",\"type\":\"address\"}],\"name\":\"migrateToNewMinter\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"addresspayable\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"trustedWithdrawETH\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"currentMintedTokens\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"getController\",\"outputs\":[{\"internalType\":\"contractIController\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"getGlobalTotalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_targetBondingRate\",\"type\":\"uint256\"}],\"name\":\"setTargetBondingRate\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_fracNum\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_fracDenom\",\"type\":\"uint256\"}],\"name\":\"createReward\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"targetBondingRate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_controller\",\"type\":\"address\"}],\"name\":\"setController\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"currentMintableTokens\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"inflationChange\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"inflation\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"trustedBurnTokens\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"trustedTransferTokens\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"setCurrentRewardTokens\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"depositETH\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"controller\",\"outputs\":[{\"internalType\":\"contractIController\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_controller\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_inflation\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_inflationChange\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_targetBondingRate\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"currentMintableTokens\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"currentInflation\",\"type\":\"uint256\"}],\"name\":\"SetCurrentRewardTokens\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"controller\",\"type\":\"address\"}],\"name\":\"SetController\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"param\",\"type\":\"string\"}],\"name\":\"ParameterUpdate\",\"type\":\"event\"}]", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_controller\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_inflation\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_inflationChange\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_targetBondingRate\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"param\",\"type\":\"string\"}],\"name\":\"ParameterUpdate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"controller\",\"type\":\"address\"}],\"name\":\"SetController\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"currentMintableTokens\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"currentInflation\",\"type\":\"uint256\"}],\"name\":\"SetCurrentRewardTokens\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"controller\",\"outputs\":[{\"internalType\":\"contractIController\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_fracNum\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_fracDenom\",\"type\":\"uint256\"}],\"name\":\"createReward\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"currentMintableTokens\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"currentMintedTokens\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"depositETH\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getController\",\"outputs\":[{\"internalType\":\"contractIController\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getGlobalTotalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"inflation\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"inflationChange\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contractIMinter\",\"name\":\"_newMinter\",\"type\":\"address\"}],\"name\":\"migrateToNewMinter\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_controller\",\"type\":\"address\"}],\"name\":\"setController\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"setCurrentRewardTokens\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_inflationChange\",\"type\":\"uint256\"}],\"name\":\"setInflationChange\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_targetBondingRate\",\"type\":\"uint256\"}],\"name\":\"setTargetBondingRate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"targetBondingRate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"trustedBurnTokens\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"trustedTransferTokens\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"addresspayable\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"trustedWithdrawETH\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", } // MinterABI is the input ABI used to generate the binding from. @@ -134,11 +135,11 @@ func NewMinterFilterer(address common.Address, filterer bind.ContractFilterer) ( // bindMinter binds a generic wrapper to an already deployed contract. func bindMinter(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { - parsed, err := abi.JSON(strings.NewReader(MinterABI)) + parsed, err := MinterMetaData.GetAbi() if err != nil { return nil, err } - return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil } // Call invokes the (constant) contract method with params as input values and diff --git a/eth/contracts/poll.go b/eth/contracts/poll.go index 28fd719c8a..5df0a2ffc8 100644 --- a/eth/contracts/poll.go +++ b/eth/contracts/poll.go @@ -26,11 +26,12 @@ var ( _ = common.Big1 _ = types.BloomLookup _ = event.NewSubscription + _ = abi.ConvertType ) // PollMetaData contains all meta data concerning the Poll contract. var PollMetaData = &bind.MetaData{ - ABI: "[{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_choiceID\",\"type\":\"uint256\"}],\"name\":\"vote\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"endBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"destroy\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_endBlock\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"voter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"choiceID\",\"type\":\"uint256\"}],\"name\":\"Vote\",\"type\":\"event\"}]", + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_endBlock\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"voter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"choiceID\",\"type\":\"uint256\"}],\"name\":\"Vote\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"destroy\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"endBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_choiceID\",\"type\":\"uint256\"}],\"name\":\"vote\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", } // PollABI is the input ABI used to generate the binding from. @@ -134,11 +135,11 @@ func NewPollFilterer(address common.Address, filterer bind.ContractFilterer) (*P // bindPoll binds a generic wrapper to an already deployed contract. func bindPoll(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { - parsed, err := abi.JSON(strings.NewReader(PollABI)) + parsed, err := PollMetaData.GetAbi() if err != nil { return nil, err } - return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil } // Call invokes the (constant) contract method with params as input values and diff --git a/eth/contracts/roundsManager.go b/eth/contracts/roundsManager.go index 6932530636..45b87d643b 100644 --- a/eth/contracts/roundsManager.go +++ b/eth/contracts/roundsManager.go @@ -26,11 +26,12 @@ var ( _ = common.Big1 _ = types.BloomLookup _ = event.NewSubscription + _ = abi.ConvertType ) // RoundsManagerMetaData contains all meta data concerning the RoundsManager contract. var RoundsManagerMetaData = &bind.MetaData{ - ABI: "[{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_roundLockAmount\",\"type\":\"uint256\"}],\"name\":\"setRoundLockAmount\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"lastRoundLengthUpdateRound\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_lip\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_round\",\"type\":\"uint256\"}],\"name\":\"setLIPUpgradeRound\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"lipUpgradeRound\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"currentRoundInitialized\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_round\",\"type\":\"uint256\"}],\"name\":\"blockHashForRound\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"targetContractId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"lastRoundLengthUpdateStartBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_roundLength\",\"type\":\"uint256\"}],\"name\":\"setRoundLength\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"currentRoundLocked\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_block\",\"type\":\"uint256\"}],\"name\":\"blockHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"lastInitializedRound\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"currentRound\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"blockNum\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"roundLength\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"currentRoundStartBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_controller\",\"type\":\"address\"}],\"name\":\"setController\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"initializeRound\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"roundLockAmount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"controller\",\"outputs\":[{\"internalType\":\"contractIController\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_controller\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"round\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"name\":\"NewRound\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"controller\",\"type\":\"address\"}],\"name\":\"SetController\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"param\",\"type\":\"string\"}],\"name\":\"ParameterUpdate\",\"type\":\"event\"}]", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_controller\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"round\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"name\":\"NewRound\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"param\",\"type\":\"string\"}],\"name\":\"ParameterUpdate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"controller\",\"type\":\"address\"}],\"name\":\"SetController\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_block\",\"type\":\"uint256\"}],\"name\":\"blockHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_round\",\"type\":\"uint256\"}],\"name\":\"blockHashForRound\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"blockNum\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"controller\",\"outputs\":[{\"internalType\":\"contractIController\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"currentRound\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"currentRoundInitialized\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"currentRoundLocked\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"currentRoundStartBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"initializeRound\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastInitializedRound\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastRoundLengthUpdateRound\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastRoundLengthUpdateStartBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"lipUpgradeRound\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"roundLength\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"roundLockAmount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_controller\",\"type\":\"address\"}],\"name\":\"setController\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_lip\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_round\",\"type\":\"uint256\"}],\"name\":\"setLIPUpgradeRound\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_roundLength\",\"type\":\"uint256\"}],\"name\":\"setRoundLength\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_roundLockAmount\",\"type\":\"uint256\"}],\"name\":\"setRoundLockAmount\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"targetContractId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", } // RoundsManagerABI is the input ABI used to generate the binding from. @@ -134,11 +135,11 @@ func NewRoundsManagerFilterer(address common.Address, filterer bind.ContractFilt // bindRoundsManager binds a generic wrapper to an already deployed contract. func bindRoundsManager(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { - parsed, err := abi.JSON(strings.NewReader(RoundsManagerABI)) + parsed, err := RoundsManagerMetaData.GetAbi() if err != nil { return nil, err } - return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil } // Call invokes the (constant) contract method with params as input values and diff --git a/eth/contracts/serviceRegistry.go b/eth/contracts/serviceRegistry.go index 21918d7022..a45d245722 100644 --- a/eth/contracts/serviceRegistry.go +++ b/eth/contracts/serviceRegistry.go @@ -26,11 +26,12 @@ var ( _ = common.Big1 _ = types.BloomLookup _ = event.NewSubscription + _ = abi.ConvertType ) // ServiceRegistryMetaData contains all meta data concerning the ServiceRegistry contract. var ServiceRegistryMetaData = &bind.MetaData{ - ABI: "[{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"getServiceURI\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"targetContractId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"string\",\"name\":\"_serviceURI\",\"type\":\"string\"}],\"name\":\"setServiceURI\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_controller\",\"type\":\"address\"}],\"name\":\"setController\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"controller\",\"outputs\":[{\"internalType\":\"contractIController\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_controller\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"serviceURI\",\"type\":\"string\"}],\"name\":\"ServiceURIUpdate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"controller\",\"type\":\"address\"}],\"name\":\"SetController\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"param\",\"type\":\"string\"}],\"name\":\"ParameterUpdate\",\"type\":\"event\"}]", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_controller\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"param\",\"type\":\"string\"}],\"name\":\"ParameterUpdate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"serviceURI\",\"type\":\"string\"}],\"name\":\"ServiceURIUpdate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"controller\",\"type\":\"address\"}],\"name\":\"SetController\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"controller\",\"outputs\":[{\"internalType\":\"contractIController\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"getServiceURI\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_controller\",\"type\":\"address\"}],\"name\":\"setController\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_serviceURI\",\"type\":\"string\"}],\"name\":\"setServiceURI\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"targetContractId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", } // ServiceRegistryABI is the input ABI used to generate the binding from. @@ -134,11 +135,11 @@ func NewServiceRegistryFilterer(address common.Address, filterer bind.ContractFi // bindServiceRegistry binds a generic wrapper to an already deployed contract. func bindServiceRegistry(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { - parsed, err := abi.JSON(strings.NewReader(ServiceRegistryABI)) + parsed, err := ServiceRegistryMetaData.GetAbi() if err != nil { return nil, err } - return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil } // Call invokes the (constant) contract method with params as input values and diff --git a/eth/contracts/ticketBroker.go b/eth/contracts/ticketBroker.go index 7b311d7f98..6604b64cd5 100644 --- a/eth/contracts/ticketBroker.go +++ b/eth/contracts/ticketBroker.go @@ -26,6 +26,7 @@ var ( _ = common.Big1 _ = types.BloomLookup _ = event.NewSubscription + _ = abi.ConvertType ) // MReserveReserveInfo is an auto generated low-level Go binding around an user-defined struct. @@ -53,7 +54,7 @@ type MixinTicketBrokerCoreSender struct { // TicketBrokerMetaData contains all meta data concerning the TicketBroker contract. var TicketBrokerMetaData = &bind.MetaData{ - ABI: "[{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_sender\",\"type\":\"address\"}],\"name\":\"isUnlockInProgress\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"unlockPeriod\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"withdraw\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_unlockPeriod\",\"type\":\"uint256\"}],\"name\":\"setUnlockPeriod\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_reserveHolder\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_claimant\",\"type\":\"address\"}],\"name\":\"claimedReserve\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_depositAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_reserveAmount\",\"type\":\"uint256\"}],\"name\":\"fundDepositAndReserve\",\"outputs\":[],\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"targetContractId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"usedTickets\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_reserveHolder\",\"type\":\"address\"}],\"name\":\"getReserveInfo\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"fundsRemaining\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"claimedInCurrentRound\",\"type\":\"uint256\"}],\"internalType\":\"structMReserve.ReserveInfo\",\"name\":\"info\",\"type\":\"tuple\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"fundDeposit\",\"outputs\":[],\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"fundReserve\",\"outputs\":[],\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_reserveHolder\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_claimant\",\"type\":\"address\"}],\"name\":\"claimableReserve\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"ticketValidityPeriod\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_controller\",\"type\":\"address\"}],\"name\":\"setController\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_depositAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_reserveAmount\",\"type\":\"uint256\"}],\"name\":\"fundDepositAndReserveFor\",\"outputs\":[],\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"unlock\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"faceValue\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"winProb\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"senderNonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"recipientRandHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"auxData\",\"type\":\"bytes\"}],\"internalType\":\"structMTicketBrokerCore.Ticket\",\"name\":\"_ticket\",\"type\":\"tuple\"}],\"name\":\"getTicketHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"cancelUnlock\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_ticketValidityPeriod\",\"type\":\"uint256\"}],\"name\":\"setTicketValidityPeriod\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"faceValue\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"winProb\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"senderNonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"recipientRandHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"auxData\",\"type\":\"bytes\"}],\"internalType\":\"structMTicketBrokerCore.Ticket[]\",\"name\":\"_tickets\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes[]\",\"name\":\"_sigs\",\"type\":\"bytes[]\"},{\"internalType\":\"uint256[]\",\"name\":\"_recipientRands\",\"type\":\"uint256[]\"}],\"name\":\"batchRedeemWinningTickets\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_sender\",\"type\":\"address\"}],\"name\":\"getSenderInfo\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"deposit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"withdrawRound\",\"type\":\"uint256\"}],\"internalType\":\"structMixinTicketBrokerCore.Sender\",\"name\":\"sender\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"fundsRemaining\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"claimedInCurrentRound\",\"type\":\"uint256\"}],\"internalType\":\"structMReserve.ReserveInfo\",\"name\":\"reserve\",\"type\":\"tuple\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"faceValue\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"winProb\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"senderNonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"recipientRandHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"auxData\",\"type\":\"bytes\"}],\"internalType\":\"structMTicketBrokerCore.Ticket\",\"name\":\"_ticket\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"_sig\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"_recipientRand\",\"type\":\"uint256\"}],\"name\":\"redeemWinningTicket\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"controller\",\"outputs\":[{\"internalType\":\"contractIController\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_controller\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"DepositFunded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"faceValue\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"winProb\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"senderNonce\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"recipientRand\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"auxData\",\"type\":\"bytes\"}],\"name\":\"WinningTicketRedeemed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"WinningTicketTransfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"startRound\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"endRound\",\"type\":\"uint256\"}],\"name\":\"Unlock\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"UnlockCancelled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"deposit\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"reserve\",\"type\":\"uint256\"}],\"name\":\"Withdrawal\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"reserveHolder\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"ReserveFunded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"reserveHolder\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"claimant\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"ReserveClaimed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"controller\",\"type\":\"address\"}],\"name\":\"SetController\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"param\",\"type\":\"string\"}],\"name\":\"ParameterUpdate\",\"type\":\"event\"}]", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_controller\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"DepositFunded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"param\",\"type\":\"string\"}],\"name\":\"ParameterUpdate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"reserveHolder\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"claimant\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"ReserveClaimed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"reserveHolder\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"ReserveFunded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"controller\",\"type\":\"address\"}],\"name\":\"SetController\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"startRound\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"endRound\",\"type\":\"uint256\"}],\"name\":\"Unlock\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"UnlockCancelled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"faceValue\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"winProb\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"senderNonce\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"recipientRand\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"auxData\",\"type\":\"bytes\"}],\"name\":\"WinningTicketRedeemed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"WinningTicketTransfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"deposit\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"reserve\",\"type\":\"uint256\"}],\"name\":\"Withdrawal\",\"type\":\"event\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"faceValue\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"winProb\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"senderNonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"recipientRandHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"auxData\",\"type\":\"bytes\"}],\"internalType\":\"structMTicketBrokerCore.Ticket[]\",\"name\":\"_tickets\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes[]\",\"name\":\"_sigs\",\"type\":\"bytes[]\"},{\"internalType\":\"uint256[]\",\"name\":\"_recipientRands\",\"type\":\"uint256[]\"}],\"name\":\"batchRedeemWinningTickets\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"cancelUnlock\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_reserveHolder\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_claimant\",\"type\":\"address\"}],\"name\":\"claimableReserve\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_reserveHolder\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_claimant\",\"type\":\"address\"}],\"name\":\"claimedReserve\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"controller\",\"outputs\":[{\"internalType\":\"contractIController\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"fundDeposit\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_depositAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_reserveAmount\",\"type\":\"uint256\"}],\"name\":\"fundDepositAndReserve\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_depositAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_reserveAmount\",\"type\":\"uint256\"}],\"name\":\"fundDepositAndReserveFor\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"fundReserve\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_reserveHolder\",\"type\":\"address\"}],\"name\":\"getReserveInfo\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"fundsRemaining\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"claimedInCurrentRound\",\"type\":\"uint256\"}],\"internalType\":\"structMReserve.ReserveInfo\",\"name\":\"info\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_sender\",\"type\":\"address\"}],\"name\":\"getSenderInfo\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"deposit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"withdrawRound\",\"type\":\"uint256\"}],\"internalType\":\"structMixinTicketBrokerCore.Sender\",\"name\":\"sender\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"fundsRemaining\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"claimedInCurrentRound\",\"type\":\"uint256\"}],\"internalType\":\"structMReserve.ReserveInfo\",\"name\":\"reserve\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"faceValue\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"winProb\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"senderNonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"recipientRandHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"auxData\",\"type\":\"bytes\"}],\"internalType\":\"structMTicketBrokerCore.Ticket\",\"name\":\"_ticket\",\"type\":\"tuple\"}],\"name\":\"getTicketHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_sender\",\"type\":\"address\"}],\"name\":\"isUnlockInProgress\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"faceValue\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"winProb\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"senderNonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"recipientRandHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"auxData\",\"type\":\"bytes\"}],\"internalType\":\"structMTicketBrokerCore.Ticket\",\"name\":\"_ticket\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"_sig\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"_recipientRand\",\"type\":\"uint256\"}],\"name\":\"redeemWinningTicket\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_controller\",\"type\":\"address\"}],\"name\":\"setController\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_ticketValidityPeriod\",\"type\":\"uint256\"}],\"name\":\"setTicketValidityPeriod\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_unlockPeriod\",\"type\":\"uint256\"}],\"name\":\"setUnlockPeriod\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"targetContractId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"ticketValidityPeriod\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unlock\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unlockPeriod\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"usedTickets\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", } // TicketBrokerABI is the input ABI used to generate the binding from. @@ -157,11 +158,11 @@ func NewTicketBrokerFilterer(address common.Address, filterer bind.ContractFilte // bindTicketBroker binds a generic wrapper to an already deployed contract. func bindTicketBroker(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { - parsed, err := abi.JSON(strings.NewReader(TicketBrokerABI)) + parsed, err := TicketBrokerMetaData.GetAbi() if err != nil { return nil, err } - return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil } // Call invokes the (constant) contract method with params as input values and diff --git a/eth/stubclient.go b/eth/stubclient.go index 50f58e3940..a5204ef4dd 100644 --- a/eth/stubclient.go +++ b/eth/stubclient.go @@ -233,6 +233,15 @@ func (m *MockClient) ReplaceTransaction(tx *types.Transaction, method string, ga func (m *MockClient) Vote(pollAddr ethcommon.Address, choiceID *big.Int) (*types.Transaction, error) { args := m.Called() return mockTransaction(args, 0), args.Error(1) +} + +func (m *MockClient) ProposalVote(proposalId *big.Int, support uint8) (*types.Transaction, error) { + args := m.Called(proposalId, support) + return mockTransaction(args, 0), args.Error(1) +} + +func (m *MockClient) ProposalVoteWithReason(proposalId *big.Int, support uint8, reason string) (*types.Transaction, error) { + args := m.Called(proposalId, support, reason) return mockTransaction(args, 0), args.Error(1) } @@ -444,3 +453,9 @@ func (c *StubClient) NextValidRequest(common.Address) (*big.Int, error) { return func (c *StubClient) Vote(pollAddr ethcommon.Address, choiceID *big.Int) (*types.Transaction, error) { return types.NewTx(&types.DynamicFeeTx{}), c.Err } +func (c *StubClient) ProposalVote(proposalId *big.Int, support uint8) (*types.Transaction, error) { + return types.NewTx(&types.DynamicFeeTx{}), c.Err +} +func (c *StubClient) ProposalVoteWithReason(proposalId *big.Int, support uint8, reason string) (*types.Transaction, error) { + return types.NewTx(&types.DynamicFeeTx{}), c.Err +} diff --git a/eth/types/contracts.go b/eth/types/contracts.go index f4744ea67e..a912d34974 100644 --- a/eth/types/contracts.go +++ b/eth/types/contracts.go @@ -125,6 +125,33 @@ func (v VoteChoice) IsValid() bool { return v == Yes || v == No } +type ProposalVoteChoice int + +const ( + Against ProposalVoteChoice = iota + For + Abstain +) + +var ProposalVoteChoices = []ProposalVoteChoice{Against, For, Abstain} + +func (v ProposalVoteChoice) String() string { + switch v { + case Against: + return "Against" + case For: + return "For" + case Abstain: + return "Abstain" + default: + return "" + } +} + +func (v ProposalVoteChoice) IsValid() bool { + return v == Against || v == For || v == Abstain +} + type TranscoderPoolHints struct { PosNext common.Address PosPrev common.Address diff --git a/go.mod b/go.mod index 0e82044924..5159948f58 100644 --- a/go.mod +++ b/go.mod @@ -9,13 +9,12 @@ require ( github.com/dustin/go-humanize v1.0.1 github.com/ethereum/go-ethereum v1.13.5 github.com/getkin/kin-openapi v0.128.0 - github.com/golang/glog v1.2.1 + github.com/golang/glog v1.2.2 github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.4 github.com/google/uuid v1.6.0 github.com/jaypipes/ghw v0.10.0 github.com/jaypipes/pcidb v1.0.0 - github.com/livepeer/ai-worker v0.13.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-20250118014304-79e6dcf08057 @@ -29,21 +28,49 @@ require ( github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.14.0 github.com/segmentio/kafka-go v0.4.47 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 github.com/testcontainers/testcontainers-go v0.34.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.28.0 - golang.org/x/sys v0.27.0 - google.golang.org/grpc v1.65.0 - google.golang.org/protobuf v1.34.1 + golang.org/x/net v0.34.0 + golang.org/x/sys v0.29.0 + google.golang.org/grpc v1.68.1 + google.golang.org/protobuf v1.35.2 pgregory.net/rapid v1.1.0 ) +require ( + github.com/asticode/go-astikit v0.30.0 // indirect + github.com/asticode/go-astits v1.13.0 // indirect + github.com/bluenviron/gortsplib/v4 v4.12.3 // indirect + github.com/bluenviron/mediacommon/v2 v2.0.0 // indirect + github.com/pion/datachannel v1.5.10 // indirect + github.com/pion/dtls/v3 v3.0.4 // indirect + github.com/pion/ice/v4 v4.0.7 // indirect + github.com/pion/interceptor v0.1.37 // indirect + github.com/pion/logging v0.2.3 // indirect + github.com/pion/mdns/v2 v2.0.7 // indirect + github.com/pion/randutil v0.1.0 // indirect + github.com/pion/rtcp v1.2.15 // indirect + github.com/pion/rtp v1.8.11 // indirect + github.com/pion/sctp v1.8.36 // indirect + github.com/pion/sdp/v3 v3.0.10 // indirect + github.com/pion/sdp/v4 v4.0.0-20240223200530-fb77fb3c6578 // indirect + github.com/pion/srtp/v3 v3.0.4 // indirect + github.com/pion/stun/v3 v3.0.0 // indirect + github.com/pion/transport/v3 v3.0.7 // indirect + github.com/pion/turn/v4 v4.0.0 // indirect + github.com/pion/webrtc/v4 v4.0.11 // indirect + github.com/wlynxg/anet v0.0.5 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 // indirect + go.opentelemetry.io/otel/sdk v1.33.0 // indirect +) + require ( cloud.google.com/go v0.110.8 // indirect - cloud.google.com/go/compute/metadata v0.3.0 // indirect + cloud.google.com/go/compute/metadata v0.5.0 // indirect cloud.google.com/go/iam v1.1.2 // indirect cloud.google.com/go/storage v1.30.1 // indirect dario.cat/mergo v1.0.0 // indirect @@ -58,7 +85,7 @@ require ( 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/cenkalti/backoff/v4 v4.3.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 @@ -77,12 +104,12 @@ require ( 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/deepmap/oapi-codegen/v2 v2.2.0 github.com/distribution/reference v0.6.0 // indirect github.com/dlclark/regexp2 v1.7.0 // indirect - github.com/docker/cli v27.3.1+incompatible // indirect - github.com/docker/docker v27.3.1+incompatible // indirect - github.com/docker/go-connections v0.5.0 // indirect + github.com/docker/cli v27.3.1+incompatible + github.com/docker/docker v27.3.1+incompatible + github.com/docker/go-connections v0.5.0 github.com/docker/go-units v0.5.0 // indirect github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127 // indirect github.com/ethereum/c-kzg-4844 v0.4.0 // indirect @@ -92,7 +119,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.1.0 // indirect + github.com/go-chi/chi/v5 v5.1.0 github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-logr/logr v1.4.2 // indirect @@ -188,7 +215,7 @@ require ( 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 // indirect + github.com/opencontainers/image-spec v1.1.0 github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 // indirect @@ -204,7 +231,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/rogpeppe/go-internal v1.12.0 // indirect + github.com/rogpeppe/go-internal v1.13.1 // 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 @@ -222,33 +249,31 @@ require ( github.com/tyler-smith/go-bip39 v1.1.0 // indirect github.com/ugorji/go/codec v1.2.11 // indirect github.com/urfave/cli/v2 v2.25.7 // indirect - github.com/vincent-petithory/dataurl v1.0.0 // indirect + github.com/vincent-petithory/dataurl v1.0.0 github.com/whyrusleeping/cbor-gen v0.0.0-20230418232409-daab9ece03a0 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 // indirect - go.opentelemetry.io/otel v1.32.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect - go.opentelemetry.io/otel/metric v1.32.0 // indirect - go.opentelemetry.io/otel/trace v1.32.0 // indirect - go.opentelemetry.io/proto/otlp v1.0.0 // indirect + go.opentelemetry.io/otel v1.33.0 // indirect + go.opentelemetry.io/otel/metric v1.33.0 // indirect + go.opentelemetry.io/otel/trace v1.33.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.26.0 // indirect + golang.org/x/crypto v0.32.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/mod v0.20.0 // indirect - golang.org/x/oauth2 v0.20.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/text v0.17.0 // indirect + golang.org/x/oauth2 v0.24.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.5.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.128.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230920204549-e6e6cdab5c13 // 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-20241209162323-e6fa225c2576 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // 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 1f5416e8f8..1a8db2b457 100644 --- a/go.sum +++ b/go.sum @@ -21,8 +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/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/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= +cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= 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.2 h1:gacbrBdWcoVmGLozRuStX45YKvJtzIjJdAolzUs1sm4= @@ -82,6 +82,10 @@ github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kd 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/asticode/go-astikit v0.30.0 h1:DkBkRQRIxYcknlaU7W7ksNfn4gMFsB0tqMJflxkRsZA= +github.com/asticode/go-astikit v0.30.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0= +github.com/asticode/go-astits v1.13.0 h1:XOgkaadfZODnyZRR5Y0/DWkA9vrkLLPLeeOvDwfKZ1c= +github.com/asticode/go-astits v1.13.0/go.mod h1:QSHmknZ51pf6KJdHKZHJTLlMegIrhega3LPWz3ND/iI= 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= @@ -94,6 +98,11 @@ 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/bluenviron/gortsplib/v4 v4.12.3 h1:3EzbyGb5+MIOJQYiWytRegFEP4EW5paiyTrscQj63WE= +github.com/bluenviron/gortsplib/v4 v4.12.3/go.mod h1:SkZPdaMNr+IvHt2PKRjUXxZN6FDutmSZn4eT0GmF0sk= +github.com/bluenviron/mediacommon v1.14.0 h1:lWCwOBKNKgqmspRpwpvvg3CidYm+XOc2+z/Jw7LM5dQ= +github.com/bluenviron/mediacommon/v2 v2.0.0 h1:JinZ9v2x6QeAOzA0cDA6aFe8vQuCrU8OyWEhG2iNzwY= +github.com/bluenviron/mediacommon/v2 v2.0.0/go.mod h1:iHEz1SFIet6zBwAQoh1a92vTQ3dV3LpVFbom6/SLz3k= 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= @@ -101,8 +110,8 @@ github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOF 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/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= @@ -299,8 +308,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.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= -github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY= +github.com/golang/glog v1.2.2/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= @@ -397,8 +406,8 @@ github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY4 github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI= 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= @@ -605,8 +614,6 @@ 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.13.3 h1:vcKUK56GRwiHIhz0UbNeKffFBAPtJPWuZNmcJrhAV8o= -github.com/livepeer/ai-worker v0.13.3/go.mod h1:rbcoIzQewbf5rvosCvG2M9DLg/ZMl7yMsaSce3svXFA= 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= @@ -756,6 +763,7 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108 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 v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= 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.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= @@ -784,11 +792,46 @@ github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0 github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o= +github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M= +github.com/pion/dtls/v3 v3.0.4 h1:44CZekewMzfrn9pmGrj5BNnTMDCFwr+6sLH+cCuLM7U= +github.com/pion/dtls/v3 v3.0.4/go.mod h1:R373CsjxWqNPf6MEkfdy3aSe9niZvL/JaKlGeFphtMg= +github.com/pion/ice/v4 v4.0.7 h1:mnwuT3n3RE/9va41/9QJqN5+Bhc0H/x/ZyiVlWMw35M= +github.com/pion/ice/v4 v4.0.7/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw= +github.com/pion/interceptor v0.1.37 h1:aRA8Zpab/wE7/c0O3fh1PqY0AJI3fCSEM5lRWJVorwI= +github.com/pion/interceptor v0.1.37/go.mod h1:JzxbJ4umVTlZAf+/utHzNesY8tmRkM2lVmkS82TTj8Y= +github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI= +github.com/pion/logging v0.2.3/go.mod h1:z8YfknkquMe1csOrxK5kc+5/ZPAzMxbKLX5aXpbpC90= +github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM= +github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA= +github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= +github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= +github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo= +github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0= +github.com/pion/rtp v1.8.11 h1:17xjnY5WO5hgO6SD3/NTIUPvSFw/PbLsIJyz1r1yNIk= +github.com/pion/rtp v1.8.11/go.mod h1:8uMBJj32Pa1wwx8Fuv/AsFhn8jsgw+3rUC2PfoBZ8p4= +github.com/pion/sctp v1.8.36 h1:owNudmnz1xmhfYje5L/FCav3V9wpPRePHle3Zi+P+M0= +github.com/pion/sctp v1.8.36/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE= +github.com/pion/sdp/v3 v3.0.10 h1:6MChLE/1xYB+CjumMw+gZ9ufp2DPApuVSnDT8t5MIgA= +github.com/pion/sdp/v3 v3.0.10/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E= +github.com/pion/sdp/v4 v4.0.0-20240223200530-fb77fb3c6578 h1:1xX8RNaRCVSm1eO+crcqe4vrIdSjINeaACJlZU/Ur6E= +github.com/pion/sdp/v4 v4.0.0-20240223200530-fb77fb3c6578/go.mod h1:g1XuC3YkK+qdxz4lzEcoM3ZRHpJ2iHc4sav0m0Abahc= +github.com/pion/srtp/v3 v3.0.4 h1:2Z6vDVxzrX3UHEgrUyIGM4rRouoC7v+NiF1IHtp9B5M= +github.com/pion/srtp/v3 v3.0.4/go.mod h1:1Jx3FwDoxpRaTh1oRV8A/6G1BnFL+QI82eK4ms8EEJQ= +github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw= +github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU= +github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0= +github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= +github.com/pion/turn/v4 v4.0.0 h1:qxplo3Rxa9Yg1xXDxxH8xaqcyGUtbHYw4QSCvmFWvhM= +github.com/pion/turn/v4 v4.0.0/go.mod h1:MuPDkm15nYSklKpN8vWJ9W2M0PlyQZqYt1McGuxG7mA= +github.com/pion/webrtc/v4 v4.0.11 h1:0i7BNFH2n8LVp08q/dqM5iyZBXW4TITbD1+RwNqk/iY= +github.com/pion/webrtc/v4 v4.0.11/go.mod h1:C+5JA7KiyLyoKyGh7hVFD/HCAon3IB/tfniocpZ9JoU= 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= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.4.0/go.mod h1:NWz/XGvpEW1FyYQ7fCx4dqYBLlfTcE+A9FLAkNKqjFE= 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= @@ -839,8 +882,8 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L 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/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= 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= @@ -904,8 +947,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ 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/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/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.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= @@ -945,6 +988,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/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= +github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= @@ -977,23 +1022,25 @@ 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/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 h1:DheMAlT6POBP+gh8RUH19EOTnQIor5QE0uSRPtzCpSw= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0/go.mod h1:wZcGmeVO9nzP67aYSLDqXNWK87EZWhi7JWj1v7ZXf94= -go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= -go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 h1:cMyu9O88joYEaI47CnQkxO1XZdpoTF9fEnW2duIddhw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0/go.mod h1:6Am3rn7P9TVVeXYG+wtcGE7IE1tsQ+bP3AuWcKt/gOI= -go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= -go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= -go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= -go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= -go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= -go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= +go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= +go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0= +go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= +go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= +go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= +go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= +go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= +go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= -go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg= +go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY= 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= @@ -1027,8 +1074,10 @@ golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWP 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.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -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/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= +golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= 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= @@ -1114,8 +1163,10 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -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/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= +golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= 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= @@ -1123,8 +1174,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.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.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= @@ -1139,8 +1190,8 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ 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.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.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= @@ -1215,8 +1266,10 @@ 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.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.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= @@ -1224,8 +1277,8 @@ golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= -golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= 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= @@ -1239,8 +1292,8 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -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/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 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= @@ -1367,10 +1420,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-20230920204549-e6e6cdab5c13 h1:vlzZttNJGVqTsRFU9AmdnrcO1Znh8Ew9kCD//yjigk0= google.golang.org/genproto v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:CCviP9RmpZ1mxVr8MUjCnSiY09IbAXZxhLE6EhHIdPU= -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/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1388,8 +1441,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.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= +google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= 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= @@ -1405,8 +1458,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ 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/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= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 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/openapi.yaml b/liveai.openapi.yaml similarity index 95% rename from openapi.yaml rename to liveai.openapi.yaml index b8b08befe1..22cde97f26 100644 --- a/openapi.yaml +++ b/liveai.openapi.yaml @@ -8,9 +8,13 @@ definitions: type: object info: contact: {} + title: Live Video-To-Video AI + version: 0.0.0 paths: /live/video-to-video/{stream}/start: get: + consumes: + - multipart/form-data parameters: - description: Stream Key in: path diff --git a/media/mediamtx.go b/media/mediamtx.go index 47a30d2e77..45212177ab 100644 --- a/media/mediamtx.go +++ b/media/mediamtx.go @@ -31,8 +31,8 @@ const ( mediaMTXControlPort = "9997" mediaMTXControlTimeout = 30 * time.Second mediaMTXControlUser = "admin" - MediaMTXWebrtcSession = "webrtcSession" - MediaMTXRtmpConn = "rtmpConn" + MediaMTXWebrtcSession = "webrtcsession" + MediaMTXRtmpConn = "rtmpconn" ) func MediamtxSourceTypeToString(s string) (string, error) { @@ -77,6 +77,7 @@ func (mc *MediaMTXClient) KickInputConnection(ctx context.Context) error { if err != nil { return fmt.Errorf("failed to kick connection: %w", err) } + defer resp.Body.Close() if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusBadRequest { body, _ := io.ReadAll(resp.Body) return fmt.Errorf("kick connection failed with status code: %d body: %s", resp.StatusCode, body) @@ -100,6 +101,7 @@ func (mc *MediaMTXClient) StreamExists() (bool, error) { if err != nil { return false, fmt.Errorf("failed to get stream: %w", err) } + defer resp.Body.Close() if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusBadRequest { body, _ := io.ReadAll(resp.Body) return false, fmt.Errorf("get stream failed with status code: %d body: %s", resp.StatusCode, body) diff --git a/media/rtmp2segment.go b/media/rtmp2segment.go index 60783844e2..64ff0e7ef7 100644 --- a/media/rtmp2segment.go +++ b/media/rtmp2segment.go @@ -18,6 +18,7 @@ import ( "syscall" "time" + "github.com/cenkalti/backoff" "github.com/livepeer/go-livepeer/clog" "golang.org/x/sys/unix" ) @@ -40,6 +41,14 @@ func (ms *MediaSegmenter) RunSegmentation(ctx context.Context, in string, segmen procCtx, procCancel := context.WithCancel(context.Background()) // parent ctx is a short lived http request wg := &sync.WaitGroup{} wg.Add(1) + + clog.Infof(ctx, "Starting segmentation for %s", outFilePattern) + + // TODO processSegments needs to also be re-invoked after each retry; + // processes that don't immediately fail are not fully retryable otherwise + // + // create first named pipe to preempt races between ffmpeg and processSegments + createNamedPipe(fmt.Sprintf(outFilePattern, 0)) go func() { defer wg.Done() defer procCancel() @@ -48,14 +57,25 @@ func (ms *MediaSegmenter) RunSegmentation(ctx context.Context, in string, segmen retryCount := 0 for { - streamExists, err := ms.MediaMTXClient.StreamExists() + err := backoff.Retry(func() error { + streamExists, err := ms.MediaMTXClient.StreamExists() + if err != nil { + return fmt.Errorf("StreamExists check failed: %w", err) + } + if !streamExists { + clog.Errorf(ctx, "input stream does not exist") + return fmt.Errorf("input stream does not exist") + } + return nil + }, backoff.WithMaxRetries(newExponentialBackOff(), 3)) if err != nil { - clog.Errorf(ctx, "StreamExists check failed. err=%s", err) - } - if retryCount > 2 && !streamExists { - clog.Errorf(ctx, "Stopping segmentation, input stream does not exist. in=%s err=%s", in, err) + clog.Errorf(ctx, "Stopping segmentation in=%s err=%s", in, err) break } + if retryCount > 0 { + time.Sleep(5 * time.Second) + } + clog.Infof(ctx, "Starting segmentation. in=%s retryCount=%d", in, retryCount) cmd := exec.CommandContext(procCtx, "ffmpeg", "-i", in, "-c:a", "copy", @@ -65,18 +85,25 @@ func (ms *MediaSegmenter) RunSegmentation(ctx context.Context, in string, segmen ) output, err := cmd.CombinedOutput() if err != nil { - clog.Errorf(ctx, "Error receiving RTMP: %v", err) - clog.Infof(ctx, "Process output: %s", output) - return + clog.Errorf(ctx, "Error receiving RTMP: %v ffmpeg output: %s", err, output) + break } + clog.Infof(ctx, "Segmentation stopped, will retry. retryCount=%d ffmpeg output: %s", retryCount, output) retryCount++ - time.Sleep(5 * time.Second) } completionSignal <- true clog.Infof(ctx, "sent completion signal, now waiting") wg.Wait() } +func newExponentialBackOff() *backoff.ExponentialBackOff { + backOff := backoff.NewExponentialBackOff() + backOff.InitialInterval = 500 * time.Millisecond + backOff.MaxInterval = 5 * time.Second + backOff.Reset() + return backOff +} + func createNamedPipe(pipeName string) { err := syscall.Mkfifo(pipeName, 0666) if err != nil && !os.IsExist(err) { @@ -201,7 +228,7 @@ func processSegments(ctx context.Context, segmentHandler SegmentHandler, outFile }() pipeNum := 0 - createNamedPipe(fmt.Sprintf(outFilePattern, pipeNum)) + // first named pipe should have been created already for { pipeName := fmt.Sprintf(outFilePattern, pipeNum) diff --git a/media/rtp_segmenter.go b/media/rtp_segmenter.go new file mode 100644 index 0000000000..40a0ed5fed --- /dev/null +++ b/media/rtp_segmenter.go @@ -0,0 +1,264 @@ +package media + +import ( + "errors" + "sync" + + "github.com/bluenviron/mediacommon/v2/pkg/formats/mpegts" + "github.com/pion/webrtc/v4" +) + +// converts from rtp to segmented mpeg2ts + +type RTPTrack interface { + Codec() webrtc.RTPCodecParameters +} + +type RTPSegmenter struct { + mu sync.RWMutex + mediaWriter *MediaWriter + tracks []*trackWriter + mpegtsWriter *mpegts.Writer + ssr *SwitchableSegmentReader + audioQueue []*audioPacket + videoQueue []*videoPacket + hasAudio bool + hasVideo bool + maxQueueSize int // Maximum number of packets to buffer per queue + tsWatermark int64 // Timestamp of the last written packet +} + +type audioPacket struct { + track *trackWriter + pts int64 + data [][]byte +} + +type videoPacket struct { + track *trackWriter + pts, dts int64 + data [][]byte +} + +type trackWriter struct { + rtpTrack RTPTrack + mpegtsTrack *mpegts.Track + writeAudio func(pts int64, data [][]byte) error + writeVideo func(pts, dts int64, data [][]byte) error +} + +func NewRTPSegmenter(tracks []RTPTrack, ssr *SwitchableSegmentReader) *RTPSegmenter { + s := &RTPSegmenter{ + ssr: ssr, + maxQueueSize: 20, + } + s.tracks = s.setupTracks(tracks) + return s +} + +func (s *RTPSegmenter) StartSegment(startTs int64) { + s.mu.Lock() + defer s.mu.Unlock() + + // Flush any pending packets < startTs + s.flushQueues(startTs) + + // close any previous segment + if s.mediaWriter != nil { + s.mediaWriter.Close() + } + // re-create mpegts tracks; we dont want to reuse them + newTracks := resetMpegtsTracks(s.tracks) + writer := NewMediaWriter() + s.mpegtsWriter = mpegts.NewWriter(writer, newTracks) + s.ssr.Read(writer.MakeReader()) + s.mediaWriter = writer +} +func (s *RTPSegmenter) WriteVideo(source RTPTrack, pts, dts int64, au [][]byte) error { + s.mu.Lock() + defer s.mu.Unlock() + for _, t := range s.tracks { + if t.rtpTrack == source { + // Check if packet is too old (below low watermark) + if s.tsWatermark > 0 && dts < s.tsWatermark { + // Packet is too old, discard it + // TODO increment some metric for this connection? + return nil + } + + // Add new packet + s.videoQueue = append(s.videoQueue, &videoPacket{t, pts, dts, au}) + + return s.interleaveAndWrite() + } + } + return errors.New("no matching video track found") +} +func (s *RTPSegmenter) WriteAudio(source RTPTrack, pts int64, au [][]byte) error { + s.mu.Lock() + defer s.mu.Unlock() + for _, t := range s.tracks { + if t.rtpTrack == source { + rescaledPts := multiplyAndDivide(pts, 90000, int64(source.Codec().ClockRate)) + + // Check if packet is too old (below low watermark) + if s.tsWatermark > 0 && rescaledPts < s.tsWatermark { + // Packet is too old, discard it + // TODO increment some metric for this connection? + return nil + } + + // Add new packet + s.audioQueue = append(s.audioQueue, &audioPacket{t, rescaledPts, au}) + + return s.interleaveAndWrite() + } + } + return errors.New("no matching audio track found") +} +func (s *RTPSegmenter) CloseSegment() { + s.mu.Lock() + defer s.mu.Unlock() + s.flushQueues(0) + if s.mediaWriter != nil { + s.mediaWriter.Close() + } +} +func (s *RTPSegmenter) IsReady() bool { + s.mu.Lock() + defer s.mu.Unlock() + return s.mediaWriter != nil +} + +func (s *RTPSegmenter) setupTracks(rtpTracks []RTPTrack) []*trackWriter { + tracks := []*trackWriter{} + for _, t := range rtpTracks { + codec := t.Codec() + cw := &trackWriter{ + rtpTrack: t, + } + switch codec.MimeType { + case webrtc.MimeTypeH264: + cw.writeVideo = func(pts, dts int64, au [][]byte) error { + return s.mpegtsWriter.WriteH264(cw.mpegtsTrack, pts, dts, au) + } + s.hasVideo = true + case webrtc.MimeTypeOpus: + cw.writeAudio = func(pts int64, data [][]byte) error { + return s.mpegtsWriter.WriteOpus(cw.mpegtsTrack, pts, data) + } + s.hasAudio = true + } + tracks = append(tracks, cw) + } + return tracks +} + +func (s *RTPSegmenter) interleaveAndWrite() error { + if !s.hasAudio || !s.hasVideo { + // only have one or the other, nothing to interleave + // so flush immediately + s.flushQueues(0) + } + + for len(s.audioQueue) > 0 && len(s.videoQueue) > 0 { + var timestamp int64 + if s.videoQueue[0].dts <= s.audioQueue[0].pts { + vp := s.videoQueue[0] + s.videoQueue = s.videoQueue[1:] + timestamp = vp.dts + if err := vp.track.writeVideo(vp.pts, vp.dts, vp.data); err != nil { + return err + } + } else { + ap := s.audioQueue[0] + s.audioQueue = s.audioQueue[1:] + timestamp = ap.pts + if err := ap.track.writeAudio(ap.pts, ap.data); err != nil { + return err + } + } + + // Update low watermark with the timestamp of the packet we just wrote + s.tsWatermark = timestamp + } + + // Check queue sizes and flush oldest packets if needed + for len(s.videoQueue) > s.maxQueueSize { + // Flush out the oldest packet + vp := s.videoQueue[0] + s.videoQueue = s.videoQueue[1:] + if err := vp.track.writeVideo(vp.pts, vp.dts, vp.data); err != nil { + return err + } + // Update watermark + s.tsWatermark = vp.dts + } + + for len(s.audioQueue) > s.maxQueueSize { + // Flush out the oldest packet + ap := s.audioQueue[0] + s.audioQueue = s.audioQueue[1:] + if err := ap.track.writeAudio(ap.pts, ap.data); err != nil { + return err + } + // Update watermark + s.tsWatermark = ap.pts + } + + return nil +} + +func (s *RTPSegmenter) flushQueues(stopTs int64) error { + // NB interleaving should not be necessary at this point + // only one queue should have any data + for len(s.videoQueue) > 0 { + vp := s.videoQueue[0] + if stopTs > 0 && vp.dts >= stopTs { + break + } + s.videoQueue = s.videoQueue[1:] + if err := vp.track.writeVideo(vp.pts, vp.dts, vp.data); err != nil { + return err + } + // Update low watermark + s.tsWatermark = vp.dts + } + for len(s.audioQueue) > 0 { + ap := s.audioQueue[0] + if stopTs > 0 && ap.pts >= stopTs { + break + } + s.audioQueue = s.audioQueue[1:] + if err := ap.track.writeAudio(ap.pts, ap.data); err != nil { + return err + } + // Update low watermark + s.tsWatermark = ap.pts + } + return nil +} + +func resetMpegtsTracks(tracks []*trackWriter) []*mpegts.Track { + newTracks := []*mpegts.Track{} + for _, t := range tracks { + codec := t.rtpTrack.Codec() + switch codec.MimeType { + case webrtc.MimeTypeH264: + newTrack := &mpegts.Track{Codec: &mpegts.CodecH264{}} + t.mpegtsTrack = newTrack + newTracks = append(newTracks, newTrack) + case webrtc.MimeTypeOpus: + newTrack := &mpegts.Track{Codec: &mpegts.CodecOpus{ChannelCount: int(codec.Channels)}} + t.mpegtsTrack = newTrack + newTracks = append(newTracks, newTrack) + } + } + return newTracks +} + +func multiplyAndDivide(v, m, d int64) int64 { + secs := v / d + dec := v % d + return (secs*m + dec*m/d) +} diff --git a/media/rtp_segmenter_test.go b/media/rtp_segmenter_test.go new file mode 100644 index 0000000000..f7d6bd2081 --- /dev/null +++ b/media/rtp_segmenter_test.go @@ -0,0 +1,230 @@ +package media + +import ( + "sync" + "testing" + + "github.com/pion/webrtc/v4" + "github.com/stretchr/testify/require" +) + +type mockTrackRemote struct { + webrtc.TrackRemote + codecType string + channels uint16 +} + +func (m *mockTrackRemote) Codec() webrtc.RTPCodecParameters { + return webrtc.RTPCodecParameters{ + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: m.codecType, + Channels: m.channels, + ClockRate: 90000, // usually 48khz but say 90 to make understanding tests easier + }, + } +} + +var ( + // Create mock tracks + videoTrack = &mockTrackRemote{codecType: webrtc.MimeTypeH264} + audioTrack = &mockTrackRemote{codecType: webrtc.MimeTypeOpus, channels: 2} +) + +func TestRTPSegmenterQueueLimit(t *testing.T) { + + require := require.New(t) + ssr := NewSwitchableSegmentReader() + seg := NewRTPSegmenter([]RTPTrack{videoTrack, audioTrack}, ssr) + seg.StartSegment(0) + + // Override maxQueueSize for testing + seg.maxQueueSize = 3 + + // Test video queue limit + for i := 0; i < seg.maxQueueSize+5; i++ { + require.NoError(seg.WriteVideo(videoTrack, int64(i*1000), int64(i*1000), [][]byte{[]byte{1, 2, 3}})) + if i <= seg.maxQueueSize { + require.Equal(int64(0), seg.tsWatermark) + } else { + require.Equal(int64(i-seg.maxQueueSize)*1000, seg.tsWatermark) + } + } + + // Check that oldest packets were flushed + // Queue should be at or below max size + require.Equal(len(seg.videoQueue), seg.maxQueueSize) + // Watermark should be updated to the timestamp of the last flushed packet + require.Equal(int64(4000), seg.tsWatermark) + + // Test audio queue limit + for i := 0; i < seg.maxQueueSize+5; i++ { + require.NoError(seg.WriteAudio(audioTrack, int64(i*10000), [][]byte{[]byte{4, 5, 6}})) + if i == 0 { + // first audio write should be dropped because zero ts + require.Equal(int64(4000), seg.tsWatermark) + } else if i <= seg.maxQueueSize { + // old video should be flushed out all at once + require.Equal(int64(7000), seg.tsWatermark) + } else { + require.Equal(int64(i-seg.maxQueueSize)*10000, seg.tsWatermark) + } + } + + // Check that oldest packets were flushed + // Queue should be at or below max size + require.Equal(len(seg.audioQueue), seg.maxQueueSize) + require.Empty(seg.videoQueue) + // Watermark should be updated to the timestamp of the last flushed packet + require.Equal(int64(40000), seg.tsWatermark) +} + +func TestRTPSegmenterVideoOnly(t *testing.T) { + require := require.New(t) + ssr := NewSwitchableSegmentReader() + seg := NewRTPSegmenter([]RTPTrack{videoTrack}, ssr) + + // Verify we have video but no audio + require.True(seg.hasVideo) + require.False(seg.hasAudio) + + // Start a segment + seg.StartSegment(0) + + // Write packets in a loop and verify state after each write + for i := 0; i < 3; i++ { + ts := int64((i + 1) * 1000) + require.NoError(seg.WriteVideo(videoTrack, ts, ts, [][]byte{[]byte{byte(i + 1), byte(i + 2), byte(i + 3)}})) + require.Equal(ts, seg.tsWatermark, "Watermark should match timestamp after packet %d", i) + require.Empty(seg.videoQueue, "Video queue should be empty after packet %d", i) + } + + // Test that writing to non-existent audio track returns an error + err := seg.WriteAudio(audioTrack, 1500, [][]byte{[]byte{10, 11, 12}}) + require.Error(err) + require.Contains(err.Error(), "no matching audio track found") +} + +func TestRTPSegmenterAudioOnly(t *testing.T) { + require := require.New(t) + ssr := NewSwitchableSegmentReader() + seg := NewRTPSegmenter([]RTPTrack{audioTrack}, ssr) + + // Verify we have audio but no video + require.True(seg.hasAudio) + require.False(seg.hasVideo) + + // Start a segment + seg.StartSegment(0) + + // Write packets in a loop and verify state after each write + for i := 0; i < 3; i++ { + ts := int64((i + 1) * 1000) + require.NoError(seg.WriteAudio(audioTrack, ts, [][]byte{[]byte{byte(1), byte(2), byte(3)}})) + require.Equal(ts, seg.tsWatermark, "Watermark should match timestamp after packet %d", i) + require.Empty(seg.audioQueue, "Audio queue should be empty after packet %d", i) + } + + // Test that writing to non-existent video track returns an error + err := seg.WriteVideo(videoTrack, 1500, 1500, [][]byte{[]byte{10, 11, 12}}) + require.Error(err) + require.Contains(err.Error(), "no matching video track found") +} + +func TestRTPSegmenterConcurrency(t *testing.T) { + require := require.New(t) + ssr := NewSwitchableSegmentReader() + seg := NewRTPSegmenter([]RTPTrack{videoTrack, audioTrack}, ssr) + + // Start a segment + seg.StartSegment(0) + + const ( + videoPackets = 500 + audioPackets = 1000 + ) + + var wg sync.WaitGroup + wg.Add(2) + + // Write video packets concurrently + go func() { + defer wg.Done() + for i := 0; i < videoPackets; i++ { + ts := int64(i * 100) + require.NoError(seg.WriteVideo(videoTrack, ts, ts, [][]byte{[]byte{byte(i % 255)}})) + } + }() + + // Write audio packets concurrently + go func() { + defer wg.Done() + for i := 0; i < audioPackets; i++ { + ts := int64(i * 50) + require.NoError(seg.WriteAudio(audioTrack, ts, [][]byte{[]byte{byte(i % 255)}})) + } + }() + + // Wait for all goroutines to complete + wg.Wait() + + // Verify final state + seg.CloseSegment() + totalPackets := len(seg.audioQueue) + len(seg.videoQueue) + require.Equal(0, totalPackets, "Still had queued packets") + + // Watermark should be set to some reasonable value + require.Greater(seg.tsWatermark, int64(0), "Watermark should be greater than 0") +} + +func TestRTPSegmenterLatePacketDropping(t *testing.T) { + require := require.New(t) + ssr := NewSwitchableSegmentReader() + seg := NewRTPSegmenter([]RTPTrack{videoTrack, audioTrack}, ssr) + + // Start a segment + seg.StartSegment(0) + + // Write some packets to establish a low watermark + require.NoError(seg.WriteVideo(videoTrack, 5000, 5000, [][]byte{[]byte{1, 2, 3}})) + + require.NoError(seg.WriteAudio(audioTrack, 2000, [][]byte{[]byte{4, 5, 6}})) + + require.Equal(int64(2000), seg.tsWatermark) + require.Len(seg.videoQueue, 1) + require.Empty(seg.audioQueue) + + // Try to write a late audio packets - should be dropped + require.NoError(seg.WriteAudio(audioTrack, 1000, [][]byte{[]byte{4, 5, 6}})) + require.Equal(int64(2000), seg.tsWatermark) + require.Len(seg.videoQueue, 1) + require.Equal(int64(5000), seg.videoQueue[0].pts) + require.Empty(seg.audioQueue) + + // Try to write a late video packet - should be dropped + require.NoError(seg.WriteVideo(videoTrack, 1500, 1500, [][]byte{[]byte{1, 2, 3}})) + require.Equal(int64(2000), seg.tsWatermark) + require.Len(seg.videoQueue, 1) + require.Equal(int64(5000), seg.videoQueue[0].pts) + require.Empty(seg.audioQueue) + + // Try to write a late audio packet - should be dropped + require.NoError(seg.WriteAudio(audioTrack, 6000, [][]byte{[]byte{4, 5, 6}})) + require.Equal(int64(5000), seg.tsWatermark) + require.Empty(seg.videoQueue) + require.Len(seg.audioQueue, 1) + require.Equal(int64(6000), seg.audioQueue[0].pts) + + // Write a new packet with higher timestamp + require.NoError(seg.WriteAudio(audioTrack, 6500, [][]byte{[]byte{4, 5, 6}})) + require.Empty(seg.videoQueue) + require.Len(seg.audioQueue, 2) + require.NoError(seg.WriteAudio(audioTrack, 6600, [][]byte{[]byte{4, 5, 6}})) + require.Empty(seg.videoQueue) + require.Len(seg.audioQueue, 3) + require.Equal(int64(5000), seg.tsWatermark) // still awaiting video before flush + require.NoError(seg.WriteVideo(videoTrack, 7000, 7000, [][]byte{[]byte{1, 2, 3}})) + require.Len(seg.audioQueue, 0) + require.Len(seg.videoQueue, 1) + // Verify low watermark is updated + require.Equal(int64(7000), seg.videoQueue[0].pts) +} diff --git a/media/segment_reader.go b/media/segment_reader.go index 5804cc583c..3c5523ff34 100644 --- a/media/segment_reader.go +++ b/media/segment_reader.go @@ -23,6 +23,7 @@ func (r *EOSReader) Clone() CloneableReader { type SwitchableSegmentReader struct { mu sync.RWMutex reader SegmentHandler + seg CloneableReader } func NewSwitchableSegmentReader() *SwitchableSegmentReader { @@ -35,12 +36,18 @@ func (sr *SwitchableSegmentReader) SwitchReader(newReader SegmentHandler) { sr.mu.Lock() defer sr.mu.Unlock() sr.reader = newReader + if sr.seg != nil { + // immediately send the current segment instead of waiting for the next one + // clone since current segment may have already been partially consumed + sr.reader(sr.seg.Clone()) + } } func (sr *SwitchableSegmentReader) Read(reader CloneableReader) { - sr.mu.RLock() - defer sr.mu.RUnlock() + sr.mu.Lock() + defer sr.mu.Unlock() sr.reader(reader) + sr.seg = reader } func (sr *SwitchableSegmentReader) Close() { diff --git a/media/whip_connection.go b/media/whip_connection.go new file mode 100644 index 0000000000..7e0021adb0 --- /dev/null +++ b/media/whip_connection.go @@ -0,0 +1,94 @@ +package media + +import ( + "io" + "sync" +) + +// use this to set peerconnection state when we need it +// kinda convoluted because the peerconnection gets created async +// separate from the goroutines where it might be closed from +type WHIPConnection struct { + mu *sync.Mutex + cond *sync.Cond + peer *MediaState + closed bool +} + +func NewWHIPConnection() *WHIPConnection { + mu := &sync.Mutex{} + return &WHIPConnection{ + mu: mu, + cond: sync.NewCond(mu), + } +} + +func (w *WHIPConnection) SetWHIPConnection(p *MediaState) { + w.mu.Lock() + defer w.mu.Unlock() + w.peer = p + w.cond.Broadcast() +} + +func (w *WHIPConnection) getWHIPConnection() *MediaState { + w.mu.Lock() + defer w.mu.Unlock() + for w.peer == nil && !w.closed { + w.cond.Wait() + } + return w.peer +} + +func (w *WHIPConnection) AwaitClose() { + p := w.getWHIPConnection() + if p == nil { + return + } + p.AwaitClose() +} + +func (w *WHIPConnection) Close() { + p := w.getWHIPConnection() + if p == nil { + return + } + p.Close() + w.mu.Lock() + defer w.mu.Unlock() + w.closed = true + w.cond.Broadcast() +} + +type WHIPPeerConnection interface { + io.Closer +} + +// MediaState manages the lifecycle of a media connection +type MediaState struct { + pc WHIPPeerConnection + closeCh chan bool + once sync.Once +} + +// NewMediaState creates a new MediaState with the given peerconnection +func NewMediaState(pc WHIPPeerConnection) *MediaState { + return &MediaState{ + pc: pc, + closeCh: make(chan bool), + } +} + +// Close closes the underlying connection and signals any waiters +func (m *MediaState) Close() { + m.once.Do(func() { + if m.pc != nil { + m.pc.Close() + } + close(m.closeCh) + }) +} + +// AwaitClose blocks until the connection is closed +func (m *MediaState) AwaitClose() { + <-m.closeCh +} diff --git a/media/whip_connection_test.go b/media/whip_connection_test.go new file mode 100644 index 0000000000..49d9efa6f7 --- /dev/null +++ b/media/whip_connection_test.go @@ -0,0 +1,502 @@ +package media + +import ( + "errors" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// MockPC mocks a webrtc.PeerConnection for testing +type MockPC struct { + closeCalled bool + closeErr error +} + +func NewMockPC() *MockPC { + return &MockPC{} +} + +func (m *MockPC) Close() error { + m.closeCalled = true + return m.closeErr +} + +func (m *MockPC) WasCloseCalled() bool { + return m.closeCalled +} + +func (m *MockPC) SetCloseError(err error) { + m.closeErr = err +} + +func TestMediaState(t *testing.T) { + t.Run("NewMediaState", func(t *testing.T) { + mockPC := NewMockPC() + state := NewMediaState(mockPC) + + assert.Equal(t, mockPC, state.pc, "Closer should be set correctly") + assert.NotNil(t, state.closeCh, "CloseCh should be initialized") + }) + + t.Run("Close", func(t *testing.T) { + mockPC := NewMockPC() + state := NewMediaState(mockPC) + + state.Close() + + assert.True(t, mockPC.WasCloseCalled(), "Close should be called on the closer") + + // Test that calling Close again doesn't call the underlying Close again + mockPC.closeCalled = false + state.Close() + + assert.False(t, mockPC.WasCloseCalled(), "Close should not be called again") + }) + + t.Run("CloseWithError", func(t *testing.T) { + mockPC := NewMockPC() + mockPC.SetCloseError(errors.New("test error")) + state := NewMediaState(mockPC) + + state.Close() + + assert.True(t, mockPC.WasCloseCalled(), "Close should be called on the closer even if it returns an error") + }) + + t.Run("AwaitClose", func(t *testing.T) { + mockPC := NewMockPC() + state := NewMediaState(mockPC) + + // Start a goroutine that will close after a short delay + go func() { + time.Sleep(100 * time.Millisecond) + state.Close() + }() + + // This should block until Close is called + done := make(chan bool) + go func() { + state.AwaitClose() + done <- true + }() + + select { + case <-done: + // Test passed + case <-time.After(500 * time.Millisecond): + assert.Fail(t, "AwaitClose did not return after Close was called") + } + }) +} + +func TestNewWHIPConnection(t *testing.T) { + // Test case: Verify that NewWHIPConnection initializes correctly + conn := NewWHIPConnection() + + assert.NotNil(t, conn.mu, "Mutex should be initialized") + assert.NotNil(t, conn.cond, "Condition variable should be initialized") + assert.Nil(t, conn.peer, "Peer should be nil initially") + assert.False(t, conn.closed, "Closed should be false initially") +} + +func TestSetWHIPConnection(t *testing.T) { + // Test case: Verify that SetWHIPConnection properly sets the peer + conn := NewWHIPConnection() + mockPC := NewMockPC() + mediaState := NewMediaState(mockPC) + + conn.SetWHIPConnection(mediaState) + + assert.Equal(t, mediaState, conn.peer, "Peer should be set to mediaState") +} + +func TestGetWHIPConnection(t *testing.T) { + // Test case 1: When peer is already set + t.Run("PeerAlreadySet", func(t *testing.T) { + conn := NewWHIPConnection() + mockPC := NewMockPC() + mediaState := NewMediaState(mockPC) + + conn.SetWHIPConnection(mediaState) + result := conn.getWHIPConnection() + + assert.Equal(t, mediaState, result, "getWHIPConnection should return the set peer") + }) + + // Test case 2: When connection is closed before peer is set + t.Run("ConnectionClosedBeforePeerSet", func(t *testing.T) { + conn := NewWHIPConnection() + + // Mark as closed + conn.mu.Lock() + conn.closed = true + conn.mu.Unlock() + + result := conn.getWHIPConnection() + + if result != nil { + t.Error("Expected getWHIPConnection to return nil when closed") + } + }) + + // Test case 3: When peer is set while waiting + t.Run("PeerSetWhileWaiting", func(t *testing.T) { + conn := NewWHIPConnection() + mockPC := NewMockPC() + mediaState := NewMediaState(mockPC) + + // Start a goroutine that will set the peer after a short delay + go func() { + time.Sleep(100 * time.Millisecond) + conn.SetWHIPConnection(mediaState) + }() + + // This should block until the peer is set + result := conn.getWHIPConnection() + + assert.Equal(t, mediaState, result, "getWHIPConnection should return the peer once set") + }) + + // Test case 4: When connection is closed while waiting + t.Run("ConnectionClosedWhileWaiting", func(t *testing.T) { + conn := NewWHIPConnection() + + // Start a goroutine that will close the connection after a short delay + go func() { + time.Sleep(100 * time.Millisecond) + conn.mu.Lock() + conn.closed = true + conn.cond.Broadcast() + conn.mu.Unlock() + }() + + // This should block until the connection is closed + result := conn.getWHIPConnection() + + assert.Nil(t, result, "getWHIPConnection should return nil when closed while waiting") + }) +} + +func TestAwaitClose(t *testing.T) { + // Test case 1: When peer is nil + t.Run("NilPeer", func(t *testing.T) { + conn := NewWHIPConnection() + + // Mark as closed to avoid blocking + conn.mu.Lock() + conn.closed = true + conn.mu.Unlock() + + // This should return immediately without error + conn.AwaitClose() + // If we reach here, the test passes + }) + + // Test case 2: When peer is set and we await its closure + t.Run("AwaitPeerClosure", func(t *testing.T) { + conn := NewWHIPConnection() + mockPC := NewMockPC() + mediaState := NewMediaState(mockPC) + + conn.SetWHIPConnection(mediaState) + + // Start a goroutine that will close the peer after a short delay + go func() { + time.Sleep(100 * time.Millisecond) + mediaState.Close() + }() + + // This should block until the peer is closed + done := make(chan bool) + go func() { + conn.AwaitClose() + done <- true + }() + + select { + case <-done: + // Test passed + assert.True(t, true, "AwaitClose should return after peer is closed") + case <-time.After(500 * time.Millisecond): + assert.Fail(t, "AwaitClose did not return after peer was closed") + } + }) + + // Test case 3: Multiple goroutines awaiting closure + t.Run("MultipleAwaiters", func(t *testing.T) { + conn := NewWHIPConnection() + mockPC := NewMockPC() + mediaState := NewMediaState(mockPC) + + conn.SetWHIPConnection(mediaState) + + // Start multiple goroutines that will await closure + const numGoroutines = 12 + done := make(chan bool, numGoroutines) + + for i := 0; i < numGoroutines; i++ { + go func() { + conn.AwaitClose() + done <- true + }() + } + + // Close the peer after a short delay + go func() { + time.Sleep(100 * time.Millisecond) + mediaState.Close() + }() + + // All goroutines should be notified + for i := 0; i < numGoroutines; i++ { + select { + case <-done: + // One goroutine completed + case <-time.After(500 * time.Millisecond): + assert.Fail(t, "Not all goroutines were notified of closure") + return + } + } + }) + + // Test case 4: Awaiting after already closed + t.Run("AwaitAfterClosed", func(t *testing.T) { + conn := NewWHIPConnection() + mockPC := NewMockPC() + mediaState := NewMediaState(mockPC) + + conn.SetWHIPConnection(mediaState) + mediaState.Close() // Close immediately + + // This should return immediately + done := make(chan bool) + go func() { + conn.AwaitClose() + done <- true + }() + + select { + case <-done: + // Test passed + case <-time.After(500 * time.Millisecond): + assert.Fail(t, "AwaitClose did not return immediately for already closed peer") + } + }) + + // Test case 5: Delayed peer connection setup before closing + t.Run("DelayedPeerSetupBeforeClose", func(t *testing.T) { + conn := NewWHIPConnection() + + // Start multiple goroutines that will await closure + const numGoroutines = 12 + done := make(chan bool, numGoroutines) + + for i := 0; i < numGoroutines; i++ { + go func() { + conn.AwaitClose() + done <- true + }() + } + + // Set up peer connection after a delay + go func() { + time.Sleep(50 * time.Millisecond) + mockPC := NewMockPC() + mediaState := NewMediaState(mockPC) + conn.SetWHIPConnection(mediaState) + + // Then close it after another delay + time.Sleep(50 * time.Millisecond) + mediaState.Close() + }() + + // All goroutines should be notified + for i := 0; i < numGoroutines; i++ { + select { + case <-done: + // One goroutine completed + case <-time.After(500 * time.Millisecond): + assert.Fail(t, "Not all goroutines were notified of closure with delayed setup") + return + } + } + }) + + // Test case 6: Close without setting peer connection + t.Run("CloseWithoutPeer", func(t *testing.T) { + conn := NewWHIPConnection() + + // Start a goroutine that will await closure + done := make(chan bool) + go func() { + conn.AwaitClose() + done <- true + }() + + // Close the connection without setting a peer + go func() { + time.Sleep(100 * time.Millisecond) + conn.mu.Lock() + conn.closed = true + conn.cond.Broadcast() + conn.mu.Unlock() + }() + + select { + case <-done: + // Test passed + case <-time.After(500 * time.Millisecond): + assert.Fail(t, "AwaitClose did not return when connection was closed without a peer") + } + }) + + // Test case 7: Null peer connection + t.Run("NullPeerConnection", func(t *testing.T) { + conn := NewWHIPConnection() + mediaState := NewMediaState(nil) // Null peer connection + + conn.SetWHIPConnection(mediaState) + + // This should not panic + require.NotPanics(t, func() { + // Start a goroutine that will await closure + done := make(chan bool) + go func() { + conn.AwaitClose() + done <- true + }() + + // Close the media state + mediaState.Close() + + select { + case <-done: + // Test passed + case <-time.After(500 * time.Millisecond): + assert.Fail(t, "AwaitClose did not return for null peer connection") + } + }) + }) +} + +func TestClose(t *testing.T) { + // Test case 1: When peer is nil + t.Run("NilPeer", func(t *testing.T) { + conn := NewWHIPConnection() + + // Mark as closed to avoid blocking + conn.mu.Lock() + conn.closed = true + conn.mu.Unlock() + + // This should return immediately without error + conn.Close() + + assert.True(t, conn.closed, "Connection should be marked as closed") + }) + + // Test case 2: When peer is set + t.Run("PeerSet", func(t *testing.T) { + conn := NewWHIPConnection() + mockPC := NewMockPC() + mediaState := NewMediaState(mockPC) + + conn.SetWHIPConnection(mediaState) + conn.Close() + + assert.True(t, mockPC.WasCloseCalled(), "Close should be called on the peer") + assert.True(t, conn.closed, "Connection should be marked as closed") + }) + + // Test case 3: Verify that Close broadcasts to waiting goroutines + t.Run("BroadcastToWaiters", func(t *testing.T) { + conn := NewWHIPConnection() + mockPC := NewMockPC() + mediaState := NewMediaState(mockPC) + + conn.SetWHIPConnection(mediaState) + + // Start a goroutine that will be waiting on the condition variable + waitDone := make(chan bool) + go func() { + conn.mu.Lock() + for !conn.closed { + conn.cond.Wait() + } + conn.mu.Unlock() + waitDone <- true + }() + + // Close the connection + conn.Close() + + // Verify that the waiting goroutine was signaled + select { + case <-waitDone: + // Test passed + assert.True(t, true, "Close should broadcast to waiting goroutines") + case <-time.After(500 * time.Millisecond): + assert.Fail(t, "Close did not broadcast to waiting goroutines") + } + }) + + // Test case 4: Verify that multiple calls to Close work correctly + t.Run("MultipleCloseCalls", func(t *testing.T) { + conn := NewWHIPConnection() + mockPC := NewMockPC() + mediaState := NewMediaState(mockPC) + + conn.SetWHIPConnection(mediaState) + + // Call Close multiple times + require.NotPanics(t, func() { + conn.Close() + conn.Close() + }, "Multiple calls to Close should not panic") + }) +} + +func TestConcurrentOperations(t *testing.T) { + // Test case: Verify that concurrent operations don't cause race conditions + conn := NewWHIPConnection() + + // Start multiple goroutines that perform operations concurrently + const numGoroutines = 10 + var wg sync.WaitGroup + wg.Add(numGoroutines * 3) + + // Some goroutines set the peer + for i := 0; i < numGoroutines; i++ { + go func(i int) { + defer wg.Done() + mockPC := NewMockPC() + mediaState := NewMediaState(mockPC) + conn.SetWHIPConnection(mediaState) + }(i) + } + + // Some goroutines get the peer + for i := 0; i < numGoroutines; i++ { + go func(i int) { + defer wg.Done() + _ = conn.getWHIPConnection() + }(i) + } + + // Some goroutines close the connection + for i := 0; i < numGoroutines; i++ { + go func(i int) { + defer wg.Done() + conn.Close() + }(i) + } + + // Wait for all goroutines to complete + require.NotPanics(t, func() { + wg.Wait() + }, "Concurrent operations should not cause deadlocks or panics") +} diff --git a/media/whip_server.go b/media/whip_server.go new file mode 100644 index 0000000000..f0b54ebf24 --- /dev/null +++ b/media/whip_server.go @@ -0,0 +1,585 @@ +package media + +import ( + "context" + "crypto/rand" + "encoding/binary" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "log" + "net" + "net/http" + "os" + "strconv" + "strings" + "sync" + "time" + + "github.com/livepeer/go-livepeer/clog" + + "github.com/bluenviron/gortsplib/v4/pkg/rtpreorderer" + "github.com/bluenviron/gortsplib/v4/pkg/rtptime" + "github.com/bluenviron/mediacommon/v2/pkg/codecs/h264" + "github.com/pion/interceptor" + "github.com/pion/interceptor/pkg/intervalpli" + "github.com/pion/rtp" + "github.com/pion/rtp/codecs" + "github.com/pion/sdp/v3" + "github.com/pion/webrtc/v4" +) + +// TODO handle PATCH/PUT for ICE restarts (new Offers) and DELETE + +const keyframeInterval = 2 * time.Second // TODO make configurable? + +var ( + allowedCodecs func(*webrtc.API) + interceptors func(*webrtc.API) + settings func(*webrtc.API) +) + +// Generate a random ID for new resources +func generateID() string { + buf := make([]byte, 8) + _, _ = rand.Read(buf) + return hex.EncodeToString(buf) +} + +// Generate a random ETag (version) +func generateETag() string { + buf := make([]byte, 8) + _, _ = rand.Read(buf) + return fmt.Sprintf(`W/"%s"`, hex.EncodeToString(buf)) // Weak ETag format +} + +// ICE server configuration. +// TODO make this configurable +var WebrtcConfig = webrtc.Configuration{ + ICEServers: []webrtc.ICEServer{ + {URLs: []string{"stun:stun.l.google.com:19302"}}, + }, +} + +type WHIPServer struct { + api *webrtc.API +} + +// handleCreate implements the POST that creates a new resource. +func (s *WHIPServer) CreateWHIP(ctx context.Context, ssr *SwitchableSegmentReader, w http.ResponseWriter, r *http.Request) *MediaState { + + clog.Infof(ctx, "creating whip") + + // Must have Content-Type: application/sdp (the spec strongly recommends it) + if r.Header.Get("Content-Type") != "application/sdp" { + http.Error(w, "Unsupported Media Type, expected application/sdp", http.StatusUnsupportedMediaType) + return nil + } + + // Read the SDP offer + offerBytes, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, "Error reading offer", http.StatusBadRequest) + return nil + } + defer r.Body.Close() + + // Create a new PeerConnection + peerConnection, err := s.api.NewPeerConnection(WebrtcConfig) + if err != nil { + clog.InfofErr(ctx, "Failed to create peerconnection", err) + http.Error(w, "Failed to create PeerConnection", http.StatusInternalServerError) + return nil + } + mediaState := NewMediaState(peerConnection) + + // OnTrack callback: handle incoming media + trackCh := make(chan *webrtc.TrackRemote) + peerConnection.OnTrack(func(track *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) { + clog.Info(ctx, "New track", "codec", track.Codec().MimeType, "ssrc", track.SSRC()) + trackCh <- track + }) + + // PeerConnection state management + peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) { + clog.Info(ctx, "ice connection state changed", "state", connectionState) + if connectionState == webrtc.ICEConnectionStateFailed { + peerConnection.Close() + } else if connectionState == webrtc.ICEConnectionStateClosed { + // Business logic when PeerConnection done + } + }) + + // Setup the remote description (the incoming offer) + sdpOffer := webrtc.SessionDescription{ + Type: webrtc.SDPTypeOffer, + SDP: string(offerBytes), + } + if err := peerConnection.SetRemoteDescription(sdpOffer); err != nil { + http.Error(w, fmt.Sprintf("SetRemoteDescription failed: %v", err), http.StatusInternalServerError) + mediaState.Close() + return mediaState + } + + // Create the answer + answer, err := peerConnection.CreateAnswer(nil) + if err != nil { + http.Error(w, fmt.Sprintf("CreateAnswer failed: %v", err), http.StatusInternalServerError) + mediaState.Close() + return mediaState + } + + // Gather ICE candidates and set local description + gatherComplete := webrtc.GatheringCompletePromise(peerConnection) + if err = peerConnection.SetLocalDescription(answer); err != nil { + http.Error(w, fmt.Sprintf("SetLocalDescription failed: %v", err), http.StatusInternalServerError) + mediaState.Close() + return mediaState + } + // Wait for ICE gathering if you want the full candidate set in the SDP + <-gatherComplete + + localSDP := peerConnection.LocalDescription() + + // Create a resource ID and ETag + resourceID := generateID() + etag := generateETag() + + // Respond with 201 Created + resourceURL := fmt.Sprintf("%s/%s", getRequestURL(r), resourceID) + w.Header().Set("Content-Type", "application/sdp") + w.Header().Set("Location", resourceURL) + w.Header().Set("ETag", etag) + w.Header()["Link"] = GenICELinkHeaders(WebrtcConfig.ICEServers) + w.WriteHeader(http.StatusCreated) + + // Write the full SDP answer + if localSDP != nil { + _, _ = w.Write([]byte(localSDP.SDP)) + } + + clog.Info(ctx, "waiting for A/V") + gatherStartTime := time.Now() + + // wait for audio or video + go func() { + defer mediaState.Close() + audioTrack, videoTrack, err := gatherIncomingTracks(ctx, peerConnection, trackCh) + if err != nil { + clog.Info(ctx, "error gathering tracks", "took", time.Since(gatherStartTime), "err", err) + return + } + if videoTrack == nil { + clog.Info(ctx, "no video! disconnecting", "took", time.Since(gatherStartTime)) + return + } + if videoTrack.Codec().MimeType != webrtc.MimeTypeH264 { + clog.Info(ctx, "Expected H.264 video", "mime", videoTrack.Codec().MimeType) + return + } + tracks := []RTPTrack{videoTrack} + if audioTrack != nil { + tracks = append(tracks, audioTrack) + } + trackCodecs := make([]string, len(tracks)) + timeDecoder := rtptime.NewGlobalDecoder2() + segmenter := NewRTPSegmenter(tracks, ssr) + var wg sync.WaitGroup // to wait for all tracks to complete + for i, track := range tracks { + trackCodecs[i] = track.Codec().MimeType + wg.Add(1) + go func() { + defer wg.Done() + handleRTP(ctx, segmenter, timeDecoder, track.(*webrtc.TrackRemote)) + }() + } + gatherDuration := time.Since(gatherStartTime) + clog.Infof(ctx, "Gathered %d tracks (%s) took=%v", len(trackCodecs), strings.Join(trackCodecs, ", "), gatherDuration) + wg.Wait() + segmenter.CloseSegment() + }() + return mediaState +} + +func handleRTP(ctx context.Context, segmenter *RTPSegmenter, timeDecoder *rtptime.GlobalDecoder2, track *webrtc.TrackRemote) { + var frame rtp.Depacketizer + codec := track.Codec().MimeType + dtsExtractor := h264.NewDTSExtractor() + incomingTrack := &IncomingTrack{track: track} + isAudio := false + switch codec { + case webrtc.MimeTypeH264: + frame = &codecs.H264Packet{IsAVC: true} + case webrtc.MimeTypeOpus: + frame = &codecs.OpusPacket{} + isAudio = true + default: + clog.Info(ctx, "Unsupported codec", "mime", codec) + return + } + + ro := rtpreorderer.New() + au := [][]byte{} + for { + pkt, _, err := track.ReadRTP() + if err != nil { + clog.Info(ctx, "Track read complete or error", "track", codec, "err", err) + return + } + pkts, lost := ro.Process(pkt) + if lost > 0 { + clog.Info(ctx, "RTP lost packets", "count", lost) + } + for _, p := range pkts { + if len(p.Payload) == 0 { + // ignore empty packets + continue + } + d, err := frame.Unmarshal(p.Payload) + if err != nil { + clog.InfofErr(ctx, "Depacketizer error", err) + } + if len(d) == 0 { + // probably fragmented + continue + } + if isAudio && !segmenter.IsReady() { + // drop early audio packets until we have video + // this is a hack to force video track to lead + // so the time decoder automatically uses rescales audio to + // the 90khz video timebase which matches mpegts + continue + } + pts, ok := timeDecoder.Decode(incomingTrack, p) + if !ok { + clog.Info(ctx, "RTP: error decoding packet time") + continue + } + if isAudio { + segmenter.WriteAudio(track, pts, [][]byte{d}) + continue + } + + nalus, err := splitH264NALUs(d) + if err != nil { + clog.InfofErr(ctx, "RTP: error splitting NALUs", err) + continue + } + if len(nalus) <= 0 { + clog.Info(ctx, "empty nalus", "len", len(d), "payload", len(p.Payload), "seq", p.SequenceNumber) + continue + } + au = append(au, nalus...) + + if !p.Marker { + // frame is not complete yet + // mpegts needs complete frames, so continue + continue + } + + dts, err := dtsExtractor.Extract(au, pts) + if err != nil { + clog.Info(ctx, "RTP: error extracting DTS", "seq", p.SequenceNumber, "pkt-ts", p.Timestamp, "pts", pts, "err", err) + continue + } + + idr := h264.IsRandomAccess(au) + if idr { + segmenter.StartSegment(dts) + } + + if segmenter.IsReady() { + segmenter.WriteVideo(track, pts, dts, au) + } + + au = [][]byte{} + } + } +} + +func gatherIncomingTracks(ctx context.Context, pc *webrtc.PeerConnection, trackCh chan *webrtc.TrackRemote) (*webrtc.TrackRemote, *webrtc.TrackRemote, error) { + // Waits for video and audio + // or video + 500ms for audio - whichever comes first + videoTimeoutStr := os.Getenv("LIVE_AI_VIDEO_GATHER_TIMEOUT") + videoTimeoutSec := 5 // default 5 seconds + if videoTimeoutStr != "" { + if vt, err := strconv.Atoi(videoTimeoutStr); err == nil && vt > 0 { + videoTimeoutSec = vt + } else { + clog.InfofErr(ctx, "invalid video timeout, using default", err) + } + } + VideoTimeout := time.Duration(videoTimeoutSec) * time.Second + AudioOnlyTimeout := VideoTimeout + AudioTimeout := 500 * time.Millisecond + videoTimer := time.NewTimer(time.Duration(VideoTimeout)) + audioTimer := time.NewTimer(time.Duration(AudioOnlyTimeout)) + sdp, err := pc.RemoteDescription().Unmarshal() + if err != nil { + clog.InfofErr(ctx, "error unmarshaling remote sdp", err) + return nil, nil, fmt.Errorf("error unmarshaling sdp: %w", err) + } + expectVideo := getMediaDescriptionByType(*sdp, "video") != nil + expectAudio := getMediaDescriptionByType(*sdp, "audio") != nil + awaitingVideo := expectVideo + awaitingAudio := expectAudio + var audioTrack *webrtc.TrackRemote + var videoTrack *webrtc.TrackRemote + for { + select { + case <-videoTimer.C: + return audioTrack, nil, nil + case <-audioTimer.C: + return nil, videoTrack, nil + case track := <-trackCh: + switch track.Kind() { + case webrtc.RTPCodecTypeAudio: + if !awaitingAudio { + clog.Info(ctx, "Received unexpected audio", "expected", expectAudio, "duplicate", expectAudio && !awaitingVideo) + } else { + audioTrack = track + } + awaitingAudio = false + if !awaitingVideo { + // got audio, don't have to wait for video, so leave + return audioTrack, videoTrack, nil + } + audioTimer.Stop() + case webrtc.RTPCodecTypeVideo: + if !awaitingVideo { + clog.Info(ctx, "Received unexpected video", "expected", expectVideo, "duplicate", expectVideo && !awaitingVideo) + } else { + videoTrack = track + } + awaitingVideo = false + if !awaitingAudio { + // got video, don't have to wait for audio, so leave + return audioTrack, videoTrack, nil + } + videoTimer.Stop() + audioTimer.Stop() + audioTimer.Reset(AudioTimeout) + default: + clog.Info(ctx, "unknown track", "kind", track.Kind()) + } + } + } + return audioTrack, videoTrack, nil +} + +func getMediaDescriptionByType(sdp sdp.SessionDescription, mediaType string) *sdp.MediaDescription { + for _, md := range sdp.MediaDescriptions { + if md.MediaName.Media == mediaType { + return md + } + } + return nil +} + +// Reconstruct the request URL as a string +func getRequestURL(r *http.Request) string { + scheme := "http" + if r.TLS != nil { + scheme = "https" + } + return fmt.Sprintf("%s://%s/%s", scheme, r.Host, r.URL.Path) +} + +// split h264 nalus +func splitH264NALUs(buf []byte) ([][]byte, error) { + var parts [][]byte + offset := 0 + + for { + // If less than 4 bytes remain, we can't read the length anymore + if len(buf[offset:]) < 4 { + // If there's leftover data but not enough for a length, + // you might consider returning an error. Or if you + // expect partial data, handle that differently. + if len(buf[offset:]) != 0 { + return nil, errors.New("truncated length prefix at end of buffer") + } + // Otherwise, we're at the exact end — break gracefully + break + } + + // Read big-endian length + partLen := binary.BigEndian.Uint32(buf[offset : offset+4]) + offset += 4 + + // Check if enough bytes remain for this part + if uint32(len(buf[offset:])) < partLen { + return nil, errors.New("truncated part data, buffer ends early") + } + + // Slice out the part data + partData := buf[offset : offset+int(partLen)] + offset += int(partLen) + + // Append to our results + parts = append(parts, partData) + } + + return parts, nil +} + +func GenICELinkHeaders(iceServers []webrtc.ICEServer) []string { + // https://github.com/bluenviron/mediamtx/blob/4dfe274239a5a37198ce108250ae8db04f34cc3e/internal/protocols/whip/link_header.go#L24-L37 + ret := make([]string, len(iceServers)) + + for i, server := range iceServers { + link := "<" + server.URLs[0] + ">; rel=\"ice-server\"" + if server.Username != "" { + link += "; username=\"" + quoteCredential(server.Username) + "\"" + + "; credential=\"" + quoteCredential(server.Credential.(string)) + "\"; credential-type=\"password\"" + } + ret[i] = link + } + + return ret +} + +func quoteCredential(v string) string { + b, _ := json.Marshal(v) + s := string(b) + return s[1 : len(s)-1] +} + +func getUDPListenerAddr() (*net.UDPAddr, error) { + addrStr := os.Getenv("LIVE_AI_WHIP_ADDR") // TODO cli args? + if addrStr == "" { + // Default to all interfaces, port 7290 + return &net.UDPAddr{ + IP: net.IP{0, 0, 0, 0}, + Port: 7290, + }, nil + } + + // Handle the ":PORT" shorthand notation + if strings.HasPrefix(addrStr, ":") { + port := 0 + _, err := fmt.Sscanf(addrStr, ":%d", &port) + if err != nil { + return nil, fmt.Errorf("invalid UDP binding port: %v", err) + } + return &net.UDPAddr{ + IP: net.IP{0, 0, 0, 0}, + Port: port, + }, nil + } + + // Parse as a full IP:PORT address + udpAddr, err := net.ResolveUDPAddr("udp", addrStr) + if err != nil { + return nil, fmt.Errorf("invalid UDP address : %v", err) + } + return udpAddr, nil +} + +func genParams() (func(*webrtc.API), func(*webrtc.API), func(*webrtc.API)) { + // Taken from Pion default codecs, but limited to to Opus and H.264 + + m := &webrtc.MediaEngine{} + + // audio codecs + for _, codec := range []webrtc.RTPCodecParameters{ + { + RTPCodecCapability: webrtc.RTPCodecCapability{webrtc.MimeTypeOpus, 48000, 2, "minptime=10;useinbandfec=1", nil}, + PayloadType: 111, + }, + } { + if err := m.RegisterCodec(codec, webrtc.RTPCodecTypeAudio); err != nil { + // this should really never happen + log.Fatal("could not register default codecs", err) + } + } + + // video codecs + videoRTCPFeedback := []webrtc.RTCPFeedback{{"goog-remb", ""}, {"ccm", "fir"}, {"nack", ""}, {"nack", "pli"}} + for _, codec := range []webrtc.RTPCodecParameters{ + { + RTPCodecCapability: webrtc.RTPCodecCapability{ + webrtc.MimeTypeH264, 90000, 0, + "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f", + videoRTCPFeedback, + }, + PayloadType: 102, + }, + { + RTPCodecCapability: webrtc.RTPCodecCapability{webrtc.MimeTypeRTX, 90000, 0, "apt=102", nil}, + PayloadType: 103, + }, + + { + RTPCodecCapability: webrtc.RTPCodecCapability{ + webrtc.MimeTypeH264, 90000, 0, + "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f", + videoRTCPFeedback, + }, + PayloadType: 104, + }, + { + RTPCodecCapability: webrtc.RTPCodecCapability{webrtc.MimeTypeRTX, 90000, 0, "apt=104", nil}, + PayloadType: 105, + }, + + // TODO verify other H.264 profile-level-id such as 42e01f won't produle bframes + } { + if err := m.RegisterCodec(codec, webrtc.RTPCodecTypeVideo); err != nil { + // this should really never happen + log.Fatal("could not register default codecs", err) + } + } + ic := &interceptor.Registry{} + intervalPliFactory, err := intervalpli.NewReceiverInterceptor(intervalpli.GeneratorInterval(keyframeInterval)) + if err != nil { + log.Fatal("could not register pli intercetpr") + } + ic.Add(intervalPliFactory) + err = webrtc.RegisterDefaultInterceptors(m, ic) + if err != nil { + // this should really never happen + log.Fatal("could not register default codecs", err) + } + se := webrtc.SettingEngine{} + + // Get UDP listener address from environment or use default + udpAddr, err := getUDPListenerAddr() + if err != nil { + log.Fatal("could not get UDP listener address: ", err) + } + + udpListener, err := net.ListenUDP("udp", udpAddr) + if err != nil { + log.Fatal("could not set udp listener: ", err) + } + + se.SetICEUDPMux(webrtc.NewICEUDPMux(nil, udpListener)) + natIP := os.Getenv("LIVE_AI_NAT_IP") + if natIP != "" { + se.SetNAT1To1IPs([]string{natIP}, webrtc.ICECandidateTypeHost) + } + return webrtc.WithMediaEngine(m), webrtc.WithInterceptorRegistry(ic), webrtc.WithSettingEngine(se) +} + +func NewWHIPServer() *WHIPServer { + allowedCodecs, interceptors, settings = genParams() + return &WHIPServer{webrtc.NewAPI(allowedCodecs, interceptors, settings)} +} + +type IncomingTrack struct { + track *webrtc.TrackRemote +} + +// ClockRate returns the clock rate. Needed by rtptime.GlobalDecoder +func (t *IncomingTrack) ClockRate() int { + return int(t.track.Codec().ClockRate) +} + +// PTSEqualsDTS returns whether PTS equals DTS. Needed by rtptime.GlobalDecoder +// TODO handle bframes; look at mediamtx +func (*IncomingTrack) PTSEqualsDTS(*rtp.Packet) bool { + return true +} diff --git a/pm/recipient.go b/pm/recipient.go index 4c20281756..966353ff5a 100644 --- a/pm/recipient.go +++ b/pm/recipient.go @@ -22,9 +22,9 @@ var errInsufficientSenderReserve = errors.New("insufficient sender reserve") var maxWinProb = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(1)) // max number of sender nonces for a given recipient random hash -var maxSenderNonces = 150 +var maxSenderNonces = 600 -var paramsExpirationBlock = big.NewInt(10) +var paramsExpirationBlock = big.NewInt(40) var paramsExpiryBuffer = int64(1) var evMultiplier = big.NewInt(100) diff --git a/server/ai_http.go b/server/ai_http.go index f8e96f65a4..61cbe5df5c 100644 --- a/server/ai_http.go +++ b/server/ai_http.go @@ -23,7 +23,7 @@ import ( ethcommon "github.com/ethereum/go-ethereum/common" "github.com/getkin/kin-openapi/openapi3filter" "github.com/golang/glog" - "github.com/livepeer/ai-worker/worker" + "github.com/livepeer/go-livepeer/ai/worker" "github.com/livepeer/go-livepeer/clog" "github.com/livepeer/go-livepeer/common" "github.com/livepeer/go-livepeer/core" @@ -97,7 +97,14 @@ func (h *lphttp) StartLiveVideoToVideo() 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()) + streamID := r.Header.Get("streamID") + requestID := r.Header.Get("requestID") + + if requestID == "" { + requestID = string(core.RandomManifestID()) + } + ctx = clog.AddVal(ctx, "request_id", requestID) + ctx = clog.AddVal(ctx, "stream_id", streamID) var req worker.GenLiveVideoToVideoJSONRequestBody if err := jsonDecoder[worker.GenLiveVideoToVideoJSONRequestBody](&req, r); err != nil { @@ -231,7 +238,7 @@ func (h *lphttp) StartLiveVideoToVideo() http.Handler { } // Send request to the worker - _, err = orch.LiveVideoToVideo(ctx, requestID, workerReq) + _, err = orch.LiveVideoToVideo(ctx, requestID, streamID, workerReq) if err != nil { if monitor.Enabled { monitor.AIProcessingError(err.Error(), pipeline, modelID, ethcommon.Address{}.String()) diff --git a/server/ai_live_video.go b/server/ai_live_video.go index fbd6c53e7c..d870752263 100644 --- a/server/ai_live_video.go +++ b/server/ai_live_video.go @@ -55,6 +55,8 @@ func startTricklePublish(ctx context.Context, url *url.URL, params aiRequestPara slowOrchChecker := &SlowOrchChecker{} + firstSegment := true + params.liveParams.segmentReader.SwitchReader(func(reader media.CloneableReader) { // check for end of stream if _, eos := reader.(*media.EOSReader); eos { @@ -97,6 +99,20 @@ func startTricklePublish(ctx context.Context, url *url.URL, params aiRequestPara n, err := segment.Write(r) if err == nil { // no error, all done, let's leave + if monitor.Enabled && firstSegment { + firstSegment = false + monitor.SendQueueEventAsync("stream_trace", map[string]interface{}{ + "type": "gateway_send_first_ingest_segment", + "timestamp": time.Now().UnixMilli(), + "stream_id": params.liveParams.streamID, + "pipeline_id": params.liveParams.pipelineID, + "request_id": params.liveParams.requestID, + "orchestrator_info": map[string]interface{}{ + "address": sess.Address(), + "url": sess.Transcoder(), + }, + }) + } return } if errors.Is(err, trickle.StreamNotFoundErr) { @@ -123,6 +139,34 @@ func startTricklePublish(ctx context.Context, url *url.URL, params aiRequestPara clog.Infof(ctx, "trickle pub") } +type multiWriter struct { + ctx context.Context + writers []io.Writer + isErrLogged bool +} + +func (t *multiWriter) Write(p []byte) (n int, err error) { + success := false + for _, w := range t.writers { + bytesWritten, err := w.Write(p) + if err != nil { + if !t.isErrLogged { + clog.Errorf(t.ctx, "multiWriter error %v", err) + t.isErrLogged = true + } + } else { + success = true + n = bytesWritten + } + } + if !success { + // all writes failed, return the error + return 0, err + } + + return n, nil +} + func startTrickleSubscribe(ctx context.Context, url *url.URL, params aiRequestParams, onFistSegment func()) { // subscribe to the outputs and send them into LPMS subscriber := trickle.NewTrickleSubscriber(url.String()) @@ -131,8 +175,16 @@ func startTrickleSubscribe(ctx context.Context, url *url.URL, params aiRequestPa params.liveParams.stopPipeline(fmt.Errorf("error getting pipe for trickle-ffmpeg. url=%s %w", url, err)) return } + rMediaMTX, wMediaMTX, err := os.Pipe() + if err != nil { + params.liveParams.stopPipeline(fmt.Errorf("error getting pipe for MediaMTX trickle-ffmpeg. url=%s %w", url, err)) + return + } ctx = clog.AddVal(ctx, "url", url.Redacted()) ctx = clog.AddVal(ctx, "outputRTMPURL", params.liveParams.outputRTMPURL) + ctx = clog.AddVal(ctx, "mediaMTXOutputRTMPURL", params.liveParams.mediaMTXOutputRTMPURL) + + multiWriter := &multiWriter{ctx: ctx, writers: []io.Writer{w, wMediaMTX}} // read segments from trickle subscription go func() { @@ -140,6 +192,7 @@ func startTrickleSubscribe(ctx context.Context, url *url.URL, params aiRequestPa firstSegment := true defer w.Close() + defer wMediaMTX.Close() retries := 0 // we're trying to keep (retryPause x maxRetries) duration to fall within one output GOP length const retryPause = 300 * time.Millisecond @@ -178,7 +231,7 @@ func startTrickleSubscribe(ctx context.Context, url *url.URL, params aiRequestPa seq := trickle.GetSeq(segment) clog.V(8).Infof(ctx, "trickle subscribe read data received seq=%d", seq) - n, err := copySegment(segment, w) + n, err := copySegment(segment, multiWriter) if err != nil { params.liveParams.stopPipeline(fmt.Errorf("trickle subscribe error copying: %w", err)) return @@ -191,42 +244,51 @@ func startTrickleSubscribe(ctx context.Context, url *url.URL, params aiRequestPa } }() - go func() { - defer func() { - r.Close() - if rec := recover(); rec != nil { - // panicked, so shut down the stream and handle it - err, ok := rec.(error) - if !ok { - err = errors.New("unknown error") - } - clog.Errorf(ctx, "LPMS panic err=%v", err) - params.liveParams.stopPipeline(fmt.Errorf("LPMS panic %w", err)) - } - }() - for { - if !params.inputStreamExists() { - clog.Errorf(ctx, "Stopping output rtmp stream, input stream does not exist.") - break - } + // Studio Output ffmpeg process + go ffmpegOutput(ctx, params.liveParams.outputRTMPURL, r, params) - cmd := exec.Command("ffmpeg", - "-i", "pipe:0", - "-c:a", "copy", - "-c:v", "copy", - "-f", "flv", - params.liveParams.outputRTMPURL, - ) - cmd.Stdin = r - output, err := cmd.CombinedOutput() - if err != nil { - clog.Errorf(ctx, "Error sending RTMP out: %v", err) - clog.Infof(ctx, "Process output: %s", output) - return + // MediaMTX Output ffmpeg process + go ffmpegOutput(ctx, params.liveParams.mediaMTXOutputRTMPURL, rMediaMTX, params) +} + +func ffmpegOutput(ctx context.Context, outputUrl string, r io.ReadCloser, params aiRequestParams) { + ctx = clog.AddVal(ctx, "rtmpOut", outputUrl) + defer func() { + r.Close() + if rec := recover(); rec != nil { + // panicked, so shut down the stream and handle it + err, ok := rec.(error) + if !ok { + err = errors.New("unknown error") } - time.Sleep(5 * time.Second) + clog.Errorf(ctx, "LPMS panic err=%v", err) + params.liveParams.stopPipeline(fmt.Errorf("LPMS panic %w", err)) } }() + for { + clog.V(6).Infof(ctx, "Starting output rtmp") + if !params.inputStreamExists() { + clog.Errorf(ctx, "Stopping output rtmp stream, input stream does not exist.") + break + } + + cmd := exec.Command("ffmpeg", + "-analyzeduration", "2500000", // 2.5 seconds + "-i", "pipe:0", + "-c:a", "copy", + "-c:v", "copy", + "-f", "flv", + outputUrl, + ) + cmd.Stdin = r + output, err := cmd.CombinedOutput() + clog.Infof(ctx, "Process output: %s", output) + if err != nil { + clog.Errorf(ctx, "Error sending RTMP out: %v", err) + return + } + time.Sleep(5 * time.Second) + } } func copySegment(segment *http.Response, w io.Writer) (int64, error) { @@ -286,10 +348,28 @@ func startEventsSubscribe(ctx context.Context, url *url.URL, params aiRequestPar stream := params.liveParams.stream streamId := params.liveParams.streamID + // vars to check events periodically to ensure liveness + var ( + eventCheckInterval = 10 * time.Second + maxEventGap = 30 * time.Second + eventTicker = time.NewTicker(eventCheckInterval) + eventsDone = make(chan bool) + // remaining vars in this block must be protected by mutex + lastEventMu = &sync.Mutex{} + lastEvent = time.Now() + ) + clog.Infof(ctx, "Starting event subscription for URL: %s", url.String()) go func() { - defer time.AfterFunc(clearStreamDelay, func() { StreamStatusStore.Clear(streamId) }) + defer time.AfterFunc(clearStreamDelay, func() { + StreamStatusStore.Clear(streamId) + GatewayStatus.Clear(streamId) + }) + defer func() { + eventTicker.Stop() + eventsDone <- true + }() const maxRetries = 5 const retryPause = 300 * time.Millisecond retries := 0 @@ -329,12 +409,27 @@ func startEventsSubscribe(ctx context.Context, url *url.URL, params aiRequestPar continue } - var event map[string]interface{} - if err := json.Unmarshal(body, &event); err != nil { + var eventWrapper struct { + QueueEventType string `json:"queue_event_type"` + Event map[string]interface{} `json:"event"` + } + if err := json.Unmarshal(body, &eventWrapper); err != nil { clog.Infof(ctx, "Failed to parse JSON from events subscription: %s", err) continue } + event := eventWrapper.Event + queueEventType := eventWrapper.QueueEventType + if event == nil { + // revert this once push to prod -- If no "event" field found, treat the entire body as the event + event = make(map[string]interface{}) + if err := json.Unmarshal(body, &event); err != nil { + clog.Infof(ctx, "Failed to parse JSON as direct event: %s", err) + continue + } + queueEventType = "ai_stream_events" + } + event["stream_id"] = streamId event["request_id"] = params.liveParams.requestID event["pipeline_id"] = params.liveParams.pipelineID @@ -347,13 +442,17 @@ func startEventsSubscribe(ctx context.Context, url *url.URL, params aiRequestPar clog.V(8).Infof(ctx, "Received event for stream=%s event=%+v", stream, event) + // record the event time + lastEventMu.Lock() + lastEvent = time.Now() + lastEventMu.Unlock() + eventType, ok := event["type"].(string) if !ok { eventType = "unknown" clog.Warningf(ctx, "Received event without a type stream=%s event=%+v", stream, event) } - queueEventType := "ai_stream_events" if eventType == "status" { queueEventType = "ai_stream_status" // The large logs and params fields are only sent once and then cleared to save bandwidth. So coalesce the @@ -383,6 +482,26 @@ func startEventsSubscribe(ctx context.Context, url *url.URL, params aiRequestPar monitor.SendQueueEventAsync(queueEventType, event) } }() + + // Use events as a heartbeat of sorts: + // if no events arrive for too long, abort the job + go func() { + for { + select { + case <-eventTicker.C: + lastEventMu.Lock() + eventTime := lastEvent + lastEventMu.Unlock() + if time.Now().Sub(eventTime) > maxEventGap { + params.liveParams.stopPipeline(errors.New("timeout waiting for events")) + eventTicker.Stop() + return + } + case <-eventsDone: + return + } + } + }() } // Detect 'slow' orchs by keeping track of in-flight segments @@ -429,8 +548,13 @@ func (s *SlowOrchChecker) GetCount() int { return s.segmentCount } -func LiveErrorEventSender(ctx context.Context, event map[string]string) func(err error) { +func LiveErrorEventSender(ctx context.Context, streamID string, event map[string]string) func(err error) { return func(err error) { + GatewayStatus.Store(streamID, map[string]interface{}{ + "last_error": err.Error(), + "last_error_time": time.Now().UnixMilli(), + }) + ev := maps.Clone(event) ev["capability"] = clog.GetVal(ctx, "capability") ev["message"] = err.Error() diff --git a/server/ai_mediaserver.go b/server/ai_mediaserver.go index 51cd47e9d6..66d11b1fd3 100644 --- a/server/ai_mediaserver.go +++ b/server/ai_mediaserver.go @@ -10,6 +10,7 @@ import ( "log/slog" "net/http" "net/url" + "os" "os/exec" "strings" "time" @@ -18,7 +19,7 @@ import ( "github.com/cenkalti/backoff" "github.com/getkin/kin-openapi/openapi3filter" - "github.com/livepeer/ai-worker/worker" + "github.com/livepeer/go-livepeer/ai/worker" "github.com/livepeer/go-livepeer/clog" "github.com/livepeer/go-livepeer/common" "github.com/livepeer/go-livepeer/core" @@ -53,6 +54,9 @@ const ( Complete ImageToVideoStatus = "complete" ) +// @title Live Video-To-Video AI +// @version 0.0.0 + func startAIMediaServer(ctx context.Context, ls *LivepeerServer) error { swagger, err := worker.GetSwagger() if err != nil { @@ -85,11 +89,20 @@ func startAIMediaServer(ctx context.Context, ls *LivepeerServer) error { ls.HTTPMux.Handle("/text-to-speech", oapiReqValidator(aiMediaServerHandle(ls, jsonDecoder[worker.GenTextToSpeechJSONRequestBody], processTextToSpeech))) // This is called by the media server when the stream is ready - ls.HTTPMux.Handle("/live/video-to-video/{stream}/start", ls.StartLiveVideo()) - ls.HTTPMux.Handle("/live/video-to-video/{prefix}/{stream}/start", ls.StartLiveVideo()) - ls.HTTPMux.Handle("/live/video-to-video/{stream}/update", ls.UpdateLiveVideo()) + ls.HTTPMux.Handle("POST /live/video-to-video/{stream}/start", ls.StartLiveVideo()) + ls.HTTPMux.Handle("POST /live/video-to-video/{prefix}/{stream}/start", ls.StartLiveVideo()) + ls.HTTPMux.Handle("POST /live/video-to-video/{stream}/update", ls.UpdateLiveVideo()) ls.HTTPMux.Handle("/live/video-to-video/smoketest", ls.SmokeTestLiveVideo()) + // Configure WHIP ingest only if an addr is specified. + // TODO use a proper cli flag + if os.Getenv("LIVE_AI_WHIP_ADDR") != "" { + whipServer := media.NewWHIPServer() + ls.HTTPMux.Handle("POST /live/video-to-video/{stream}/whip", ls.CreateWhip(whipServer)) + ls.HTTPMux.Handle("HEAD /live/video-to-video/{stream}/whip", ls.WithCode(http.StatusMethodNotAllowed)) + ls.HTTPMux.Handle("OPTIONS /live/video-to-video/{stream}/whip", ls.WithCode(http.StatusNoContent)) + } + // Stream status ls.HTTPMux.Handle("/live/video-to-video/{streamId}/status", ls.GetLiveVideoToVideoStatus()) @@ -368,6 +381,7 @@ func (ls *LivepeerServer) ImageToVideoResult() http.Handler { } // @Summary Start Live Video +// @Accept multipart/form-data // @Param stream path string true "Stream Key" // @Param source_id formData string true "MediaMTX source ID, used for calls back to MediaMTX" // @Param source_type formData string true "MediaMTX specific source type (webrtcSession/rtmpConn)" @@ -384,6 +398,8 @@ func (ls *LivepeerServer) StartLiveVideo() http.Handler { return } + streamRequestTime := time.Now().UnixMilli() + ctx = clog.AddVal(ctx, "stream", streamName) sourceID := r.FormValue("source_id") if sourceID == "" { @@ -398,6 +414,7 @@ func (ls *LivepeerServer) StartLiveVideo() http.Handler { http.Error(w, "Missing source_type", http.StatusBadRequest) return } + sourceType = strings.ToLower(sourceType) // mediamtx changed casing between versions sourceTypeStr, err := media.MediamtxSourceTypeToString(sourceType) if err != nil { clog.Errorf(ctx, "Invalid source type %s", sourceType) @@ -429,6 +446,18 @@ func (ls *LivepeerServer) StartLiveVideo() http.Handler { outputURL = fmt.Sprintf("rtmp://%s/%s-out", remoteHost, streamName) } + // Currently for webrtc we need to add a path prefix due to the ingress setup + mediaMTXStreamPrefix := r.PathValue("prefix") + if mediaMTXStreamPrefix != "" { + mediaMTXStreamPrefix = mediaMTXStreamPrefix + "/" + } + mediaMTXInputURL := fmt.Sprintf("rtmp://%s/%s%s", remoteHost, mediaMTXStreamPrefix, streamName) + mediaMTXRtmpURL := r.FormValue("rtmp_url") + if mediaMTXRtmpURL != "" { + mediaMTXInputURL = mediaMTXRtmpURL + } + mediaMTXOutputURL := mediaMTXInputURL + "-out" + // convention to avoid re-subscribing to our own streams // in case we want to push outputs back into mediamtx - // use an `-out` suffix for the stream name. @@ -499,6 +528,21 @@ func (ls *LivepeerServer) StartLiveVideo() http.Handler { ctx = clog.AddVal(ctx, "stream_id", streamID) clog.Infof(ctx, "Received live video AI request for %s. pipelineParams=%v", streamName, pipelineParams) + // Clear any previous gateway status + GatewayStatus.Clear(streamID) + + monitor.SendQueueEventAsync("stream_trace", map[string]interface{}{ + "type": "gateway_receive_stream_request", + "timestamp": streamRequestTime, + "stream_id": streamID, + "pipeline_id": pipelineID, + "request_id": requestID, + "orchestrator_info": map[string]interface{}{ + "address": "", + "url": "", + }, + }) + // Count `ai_live_attempts` after successful parameters validation clog.V(common.VERBOSE).Infof(ctx, "AI Live video attempt") if monitor.Enabled { @@ -508,18 +552,13 @@ func (ls *LivepeerServer) StartLiveVideo() http.Handler { // Kick off the RTMP pull and segmentation as soon as possible ssr := media.NewSwitchableSegmentReader() go func() { - // Currently for webrtc we need to add a path prefix due to the ingress setup - mediaMTXStreamPrefix := r.PathValue("prefix") - if mediaMTXStreamPrefix != "" { - mediaMTXStreamPrefix = mediaMTXStreamPrefix + "/" - } ms := media.MediaSegmenter{Workdir: ls.LivepeerNode.WorkDir, MediaMTXClient: mediaMTXClient} - ms.RunSegmentation(ctx, fmt.Sprintf("rtmp://%s/%s%s", remoteHost, mediaMTXStreamPrefix, streamName), ssr.Read) + ms.RunSegmentation(ctx, mediaMTXInputURL, ssr.Read) ssr.Close() - ls.cleanupLive(streamName) + ls.cleanupLive(ctx, streamName) }() - sendErrorEvent := LiveErrorEventSender(ctx, map[string]string{ + sendErrorEvent := LiveErrorEventSender(ctx, streamID, map[string]string{ "type": "error", "request_id": requestID, "stream_id": streamID, @@ -533,7 +572,7 @@ func (ls *LivepeerServer) StartLiveVideo() http.Handler { if err == nil { return } - clog.Errorf(ctx, "Live video pipeline stopping: %s", err) + clog.Errorf(ctx, "Live video pipeline finished with error: %s", err) sendErrorEvent(err) @@ -551,6 +590,7 @@ func (ls *LivepeerServer) StartLiveVideo() http.Handler { liveParams: liveRequestParams{ segmentReader: ssr, outputRTMPURL: outputURL, + mediaMTXOutputRTMPURL: mediaMTXOutputURL, stream: streamName, paymentProcessInterval: ls.livePaymentInterval, requestID: requestID, @@ -641,10 +681,17 @@ func (ls *LivepeerServer) GetLiveVideoToVideoStatus() http.Handler { // Get status for specific stream status, exists := StreamStatusStore.Get(streamId) - if !exists { + gatewayStatus, gatewayExists := GatewayStatus.Get(streamId) + if !exists && !gatewayExists { http.Error(w, "Stream not found", http.StatusNotFound) return } + if gatewayExists { + if status == nil { + status = make(map[string]any) + } + status["gateway_status"] = gatewayStatus + } w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(status); err != nil { @@ -655,7 +702,171 @@ func (ls *LivepeerServer) GetLiveVideoToVideoStatus() http.Handler { }) } -func (ls *LivepeerServer) cleanupLive(stream string) { +func (ls *LivepeerServer) CreateWhip(server *media.WHIPServer) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // two main sequential parts here + // + // WHIP : stream setup, await tracks + // Others: auth, selection, job setup + // Run them in parallel + + streamRequestTime := time.Now().UnixMilli() + corsHeaders(w, r.Method) + + // NB: deliberately not using r.Context since ctx will outlive the request + ctx := context.Background() + requestID := string(core.RandomManifestID()) + ctx = clog.AddVal(ctx, "request_id", requestID) + streamName := r.PathValue("stream") + if streamName == "" { + http.Error(w, "Missing stream name", http.StatusBadRequest) + return + } + ctx = clog.AddVal(ctx, "stream", streamName) + + ssr := media.NewSwitchableSegmentReader() + + whipConn := media.NewWHIPConnection() + + go func() { + internalOutputHost := os.Getenv("LIVE_AI_PLAYBACK_HOST") // TODO proper cli arg + if internalOutputHost == "" { + internalOutputHost = "rtmp://localhost/" + } + mediamtxOutputURL := internalOutputHost + streamName + "-out" + outputURL := "" + streamID := "" + pipelineID := "" + pipeline := "" + pipelineParams := make(map[string]interface{}) + sourceTypeStr := "livepeer-whip" + queryParams := r.URL.Query().Encode() + + ctx = clog.AddVal(ctx, "source_type", sourceTypeStr) + + if LiveAIAuthWebhookURL != nil { + authResp, err := authenticateAIStream(LiveAIAuthWebhookURL, ls.liveAIAuthApiKey, AIAuthRequest{ + Stream: streamName, + Type: sourceTypeStr, + QueryParams: queryParams, + GatewayHost: ls.LivepeerNode.GatewayHost, + }) + if err != nil { + whipConn.Close() + clog.Errorf(ctx, "Live AI auth failed: %s", err.Error()) + return + } + + if authResp.RTMPOutputURL != "" { + outputURL = authResp.RTMPOutputURL + } + + if authResp.Pipeline != "" { + pipeline = authResp.Pipeline + } + + if len(authResp.paramsMap) > 0 { + if _, ok := authResp.paramsMap["prompt"]; !ok && pipeline == "comfyui" { + pipelineParams = map[string]interface{}{"prompt": authResp.paramsMap} + } else { + pipelineParams = authResp.paramsMap + } + } + + if authResp.StreamID != "" { + streamID = authResp.StreamID + ctx = clog.AddVal(ctx, "stream_id", streamID) + } + + if authResp.PipelineID != "" { + pipelineID = authResp.PipelineID + } + } + + sendErrorEvent := LiveErrorEventSender(ctx, streamID, map[string]string{ + "type": "error", + "request_id": requestID, + "stream_id": streamID, + "pipeline_id": pipelineID, + "pipeline": pipeline, + }) + stopPipeline := func(err error) { + if err == nil { + return + } + clog.Errorf(ctx, "Live video pipeline finished with error: %s", err) + sendErrorEvent(err) + whipConn.Close() + } + + clog.Info(ctx, "Received live video AI request", "pipelineParams", pipelineParams) + + // Clear any previous gateway status + GatewayStatus.Clear(streamID) + + monitor.SendQueueEventAsync("stream_trace", map[string]interface{}{ + "type": "gateway_receive_stream_request", + "timestamp": streamRequestTime, + "stream_id": streamID, + "pipeline_id": pipelineID, + "request_id": requestID, + "orchestrator_info": map[string]interface{}{ + "address": "", + "url": "", + }, + }) + + if monitor.Enabled { + monitor.AILiveVideoAttempt() + } + + params := aiRequestParams{ + node: ls.LivepeerNode, + os: drivers.NodeStorage.NewSession(requestID), + sessManager: ls.AISessionManager, + + liveParams: liveRequestParams{ + segmentReader: ssr, + outputRTMPURL: outputURL, + mediaMTXOutputRTMPURL: mediamtxOutputURL, + stream: streamName, + paymentProcessInterval: ls.livePaymentInterval, + requestID: requestID, + streamID: streamID, + pipelineID: pipelineID, + stopPipeline: stopPipeline, + sendErrorEvent: sendErrorEvent, + }, + } + + req := worker.GenLiveVideoToVideoJSONRequestBody{ + ModelId: &pipeline, + Params: &pipelineParams, + } + _, err := processAIRequest(ctx, params, req) + if err != nil { + stopPipeline(err) + } + whipConn.AwaitClose() + ssr.Close() + ls.cleanupLive(ctx, streamName) + clog.Info(ctx, "Live cleaned up") + }() + + conn := server.CreateWHIP(ctx, ssr, w, r) + whipConn.SetWHIPConnection(conn) // might be nil if theres an error and thats okay + }) +} + +func (ls *LivepeerServer) WithCode(code int) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + corsHeaders(w, r.Method) + w.WriteHeader(code) + }) +} + +func (ls *LivepeerServer) cleanupLive(ctx context.Context, stream string) { + clog.Infof(ctx, "Live video pipeline finished") ls.LivepeerNode.LiveMu.Lock() pub, ok := ls.LivepeerNode.LivePipelines[stream] delete(ls.LivepeerNode.LivePipelines, stream) @@ -672,6 +883,20 @@ func (ls *LivepeerServer) cleanupLive(stream string) { } } +func corsHeaders(w http.ResponseWriter, reqMethod string) { + if os.Getenv("LIVE_AI_ALLOW_CORS") == "" { // TODO cli arg + return + } + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Headers", "Content-Type") + if reqMethod == http.MethodOptions { + w.Header().Set("Access-Control-Allow-Methods", "OPTIONS, POST") + // Allows us send down a preferred STUN server without ICE restart + // https://datatracker.ietf.org/doc/html/draft-ietf-wish-whip-16#section-4.6 + w.Header()["Link"] = media.GenICELinkHeaders(media.WebrtcConfig.ICEServers) + } +} + const defaultSmokeTestDuration = 5 * time.Minute const maxSmokeTestDuration = 60 * time.Minute diff --git a/server/ai_pipeline_status.go b/server/ai_pipeline_status.go index 8bc951607d..ca1cf7490f 100644 --- a/server/ai_pipeline_status.go +++ b/server/ai_pipeline_status.go @@ -10,6 +10,7 @@ type streamStatusStore struct { } var StreamStatusStore = streamStatusStore{store: make(map[string]map[string]interface{})} +var GatewayStatus = streamStatusStore{store: make(map[string]map[string]interface{})} // StoreStreamStatus updates the status for a stream func (s *streamStatusStore) Store(streamID string, status map[string]interface{}) { diff --git a/server/ai_process.go b/server/ai_process.go index cd38feb2cc..0faae906f4 100644 --- a/server/ai_process.go +++ b/server/ai_process.go @@ -17,7 +17,7 @@ import ( "strings" "time" - "github.com/livepeer/ai-worker/worker" + "github.com/livepeer/go-livepeer/ai/worker" "github.com/livepeer/go-livepeer/clog" "github.com/livepeer/go-livepeer/common" "github.com/livepeer/go-livepeer/core" @@ -27,17 +27,20 @@ import ( "github.com/livepeer/lpms/stream" ) -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" -const defaultAudioToTextModelID = "openai/whisper-large-v3" -const defaultLLMModelID = "meta-llama/llama-3.1-8B-Instruct" -const defaultSegmentAnything2ModelID = "facebook/sam2-hiera-large" -const defaultImageToTextModelID = "Salesforce/blip-image-captioning-large" -const defaultLiveVideoToVideoModelID = "noop" -const defaultTextToSpeechModelID = "parler-tts/parler-tts-large-v1" +const ( + defaultTextToImageModelID = "stabilityai/sdxl-turbo" + defaultImageToImageModelID = "stabilityai/sdxl-turbo" + defaultImageToVideoModelID = "stabilityai/stable-video-diffusion-img2vid-xt" + defaultUpscaleModelID = "stabilityai/stable-diffusion-x4-upscaler" + defaultAudioToTextModelID = "openai/whisper-large-v3" + defaultLLMModelID = "meta-llama/llama-3.1-8B-Instruct" + defaultSegmentAnything2ModelID = "facebook/sam2-hiera-large" + defaultImageToTextModelID = "Salesforce/blip-image-captioning-large" + defaultLiveVideoToVideoModelID = "noop" + defaultTextToSpeechModelID = "parler-tts/parler-tts-large-v1" + + maxTries = 20 +) var errWrongFormat = fmt.Errorf("result not in correct format") @@ -98,12 +101,13 @@ func (a aiRequestParams) inputStreamExists() bool { // For live video pipelines type liveRequestParams struct { - segmentReader *media.SwitchableSegmentReader - outputRTMPURL string - stream string - requestID string - streamID string - pipelineID string + segmentReader *media.SwitchableSegmentReader + outputRTMPURL string + mediaMTXOutputRTMPURL string + stream string + requestID string + streamID string + pipelineID string paymentProcessInterval time.Duration @@ -1039,9 +1043,24 @@ func submitLiveVideoToVideo(ctx context.Context, params aiRequestParams, sess *A } return nil, err } - setHeaders, balUpdate, err := prepareAIPayment(ctx, sess, initPixelsToPay) + paymentHeaders, balUpdate, err := prepareAIPayment(ctx, sess, initPixelsToPay) + if err != nil { + if monitor.Enabled { + monitor.AIRequestError(err.Error(), "LiveVideoToVideo", *req.ModelId, sess.OrchestratorInfo) + } + return nil, err + } defer completeBalanceUpdate(sess.BroadcastSession, balUpdate) + setHeaders := func(ctx context.Context, req *http.Request) error { + if err := paymentHeaders(ctx, req); err != nil { + return err + } + req.Header.Set("requestID", params.liveParams.requestID) + req.Header.Set("streamID", params.liveParams.streamID) + return nil + } + // Send request to orchestrator resp, err := client.GenLiveVideoToVideoWithResponse(ctx, req, setHeaders) if err != nil { @@ -1082,6 +1101,17 @@ func submitLiveVideoToVideo(ctx context.Context, params aiRequestParams, sess *A delayMs := time.Since(startTime).Milliseconds() if monitor.Enabled { monitor.AIFirstSegmentDelay(delayMs, sess.OrchestratorInfo) + monitor.SendQueueEventAsync("stream_trace", map[string]interface{}{ + "type": "gateway_receive_first_processed_segment", + "timestamp": time.Now().UnixMilli(), + "stream_id": params.liveParams.streamID, + "pipeline_id": params.liveParams.pipelineID, + "request_id": params.liveParams.requestID, + "orchestrator_info": map[string]interface{}{ + "address": sess.Address(), + "url": sess.Transcoder(), + }, + }) } clog.V(common.VERBOSE).Infof(ctx, "First Segment delay=%dms streamID=%s", delayMs, params.liveParams.streamID) @@ -1464,6 +1494,7 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface } capName := cap.String() ctx = clog.AddVal(ctx, "capability", capName) + ctx = clog.AddVal(ctx, "model_id", modelID) clog.V(common.VERBOSE).Infof(ctx, "Received AI request model_id=%s", modelID) start := time.Now() @@ -1471,16 +1502,21 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface var resp interface{} + processingRetryTimeout := params.node.AIProcesssingRetryTimeout cctx, cancel := context.WithTimeout(ctx, processingRetryTimeout) defer cancel() tries := 0 - for { + var retryableSessions []*AISession + for tries < maxTries { select { case <-cctx.Done(): - err := fmt.Errorf("no orchestrators available within %v timeout", processingRetryTimeout) - if monitor.Enabled { - monitor.AIRequestError(err.Error(), monitor.ToPipeline(capName), modelID, nil) + err := cctx.Err() + if errors.Is(err, context.DeadlineExceeded) { + err = fmt.Errorf("no orchestrators available within %v timeout", processingRetryTimeout) + if monitor.Enabled { + monitor.AIRequestError(err.Error(), monitor.ToPipeline(capName), modelID, nil) + } } return nil, &ServiceUnavailableError{err: err} default: @@ -1503,6 +1539,19 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface break } + // Don't suspend the session if the error is a transient error. + if isRetryableError(err) { + params.sessManager.Complete(ctx, sess) + continue + } + + // when no capacity error is received, retry with another session, but do not suspend the session + if (isInvalidTicketSenderNonce(err) || isNoCapacityError(err)) && cap != core.Capability_LiveVideoToVideo { + retryableSessions = append(retryableSessions, sess) + continue + } + + // Suspend the session on other errors. clog.Infof(ctx, "Error submitting request modelID=%v try=%v orch=%v err=%v", modelID, tries, sess.Transcoder(), err) params.sessManager.Remove(ctx, sess) //TODO: Improve session selection logic for live-video-to-video @@ -1515,6 +1564,11 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface } } + //add retryable sessions back to selector + for _, sess := range retryableSessions { + params.sessManager.Complete(ctx, sess) + } + if resp == nil { errMsg := "no orchestrators available" if monitor.Enabled { @@ -1525,6 +1579,28 @@ func processAIRequest(ctx context.Context, params aiRequestParams, req interface return resp, nil } +// isRetryableError checks if the error is a transient error that can be retried. +func isRetryableError(err error) bool { + return errContainsMsg(err, "ticketparams expired") +} + +func isInvalidTicketSenderNonce(err error) bool { + return errContainsMsg(err, "invalid ticket sendernonce") +} + +func isNoCapacityError(err error) bool { + return errContainsMsg(err, "insufficient capacity") +} + +func errContainsMsg(err error, msgs ...string) bool { + errMsg := strings.ToLower(err.Error()) + for _, msg := range msgs { + if strings.Contains(errMsg, msg) { + return true + } + } + return false +} 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) diff --git a/server/ai_process_test.go b/server/ai_process_test.go index dd156d77f0..d308d8d347 100644 --- a/server/ai_process_test.go +++ b/server/ai_process_test.go @@ -2,10 +2,11 @@ package server import ( "context" + "errors" "reflect" "testing" - "github.com/livepeer/ai-worker/worker" + "github.com/livepeer/go-livepeer/ai/worker" ) func Test_submitLLM(t *testing.T) { @@ -123,3 +124,99 @@ func TestEncodeReqMetadata(t *testing.T) { }) } } + +func Test_isNoCapacityError(t *testing.T) { + tests := []struct { + name string + err error + want bool + }{ + { + name: "insufficient capacity error", + err: errors.New("Insufficient capacity"), + want: true, + }, + { + name: "INSUFFICIENT capacity ERROR", + err: errors.New("Insufficient capacity"), + want: true, + }, + { + name: "non-insufficient capacity error", + err: errors.New("some other error"), + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := isNoCapacityError(tt.err); got != tt.want { + t.Errorf("isNoCapacityError() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_isInvalidTicketSenderNonc(t *testing.T) { + tests := []struct { + name string + err error + want bool + }{ + { + name: "invalid ticket sendernonce", + err: errors.New("invalid ticket sendernonce"), + want: true, + }, + { + name: "INVALID ticket sendernonce", + err: errors.New("Invalid ticket sendernonce"), + want: true, + }, + { + name: "non-insufficient capacity error", + err: errors.New("some other error"), + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := isInvalidTicketSenderNonce(tt.err); got != tt.want { + t.Errorf("isNoCapacityError() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_isRetryableError(t *testing.T) { + tests := []struct { + name string + err error + want bool + }{ + { + name: "ticketparams expired", + err: errors.New("ticketparams expired"), + want: true, + }, + { + name: "TICKETPARAMS expired", + err: errors.New("TICKETPARAMS expired"), + want: true, + }, + { + name: "non-retryable error", + err: errors.New("some other error"), + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := isRetryableError(tt.err); got != tt.want { + t.Errorf("isRetryableError() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/server/ai_session.go b/server/ai_session.go index 63cb8134cc..ca56f48e34 100644 --- a/server/ai_session.go +++ b/server/ai_session.go @@ -29,14 +29,16 @@ type AISessionPool struct { sessMap map[string]*BroadcastSession inUseSess []*BroadcastSession suspender *suspender + penalty int mu sync.RWMutex } -func NewAISessionPool(selector BroadcastSessionsSelector, suspender *suspender) *AISessionPool { +func NewAISessionPool(selector BroadcastSessionsSelector, suspender *suspender, penalty int) *AISessionPool { return &AISessionPool{ selector: selector, sessMap: make(map[string]*BroadcastSession), suspender: suspender, + penalty: penalty, mu: sync.RWMutex{}, } } @@ -101,14 +103,14 @@ 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() - var uniqueSessions []*BroadcastSession for _, sess := range sessions { - if _, ok := pool.sessMap[sess.Transcoder()]; ok { - // Skip the session if it is already tracked by sessMap + if existingSess, ok := pool.sessMap[sess.Transcoder()]; ok { + // For existing sessions we only update its fields + existingSess.OrchestratorInfo = sess.OrchestratorInfo + existingSess.InitialLatency = sess.InitialLatency + existingSess.InitialPrice = sess.InitialPrice + existingSess.PMSessionID = sess.PMSessionID continue } @@ -126,10 +128,14 @@ func (pool *AISessionPool) Remove(sess *BroadcastSession) { delete(pool.sessMap, sess.Transcoder()) pool.inUseSess = removeSessionFromList(pool.inUseSess, sess) - // Magic number for now - penalty := 3 // If this method is called assume that the orch should be suspended - // as well + // as well. Since AISessionManager re-uses the pools the suspension + // penalty needs to consider the current suspender count to set the penalty + lastCount, ok := pool.suspender.list[sess.Transcoder()] + penalty := pool.suspender.count + pool.penalty + if ok { + penalty -= lastCount + } pool.suspender.suspend(sess.Transcoder(), penalty) } @@ -156,16 +162,18 @@ type AISessionSelector struct { // The time until the pools should be refreshed with orchs from discovery ttl time.Duration lastRefreshTime time.Time + initialPoolSize int cap core.Capability modelID string node *core.LivepeerNode suspender *suspender + penalty int os drivers.OSSession } -func NewAISessionSelector(cap core.Capability, modelID string, node *core.LivepeerNode, ttl time.Duration) (*AISessionSelector, error) { +func NewAISessionSelector(ctx context.Context, 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} @@ -177,11 +185,24 @@ func NewAISessionSelector(cap core.Capability, modelID string, node *core.Livepe 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, warmCaps), suspender) - coldPool := NewAISessionPool(NewMinLSSelector(stakeRdr, minLS, node.SelectionAlgorithm, node.OrchPerfScore, coldCaps), suspender) + // Session pool suspender starts at 0. Suspension is 3 requests if there are errors from the orchestrator + penalty := 3 + var warmSel, coldSel BroadcastSessionsSelector + if cap == core.Capability_LiveVideoToVideo { + // For Realtime Video AI, we don't use any features of MinLSSelector (preferring known sessions, etc.), + // We always select a fresh session which has the lowest initial latency + warmSel = NewSelector(stakeRdr, node.SelectionAlgorithm, node.OrchPerfScore, warmCaps) + coldSel = NewSelector(stakeRdr, node.SelectionAlgorithm, node.OrchPerfScore, coldCaps) + // we don't use penalties for not in Realtime Video AI + penalty = 0 + } else { + // sort sessions based on current latency score + warmSel = NewSelectorOrderByLatencyScore(stakeRdr, node.SelectionAlgorithm, node.OrchPerfScore, warmCaps) + coldSel = NewSelectorOrderByLatencyScore(stakeRdr, node.SelectionAlgorithm, node.OrchPerfScore, coldCaps) + } + + warmPool := NewAISessionPool(warmSel, suspender, penalty) + coldPool := NewAISessionPool(coldSel, suspender, penalty) sel := &AISessionSelector{ warmPool: warmPool, coldPool: coldPool, @@ -190,16 +211,43 @@ func NewAISessionSelector(cap core.Capability, modelID string, node *core.Livepe modelID: modelID, node: node, suspender: suspender, + penalty: penalty, os: drivers.NodeStorage.NewSession(strconv.Itoa(int(cap)) + "_" + modelID), } - if err := sel.Refresh(context.Background()); err != nil { + if err := sel.Refresh(ctx); err != nil { return nil, err } + // Periodically refresh sessions for Live Video to Video in order to minimize the necessity of refreshing sessions + // when the AI process is started + if cap == core.Capability_LiveVideoToVideo { + startPeriodicRefresh(sel) + } + return sel, nil } +func startPeriodicRefresh(sel *AISessionSelector) { + clog.Infof(context.Background(), "Starting periodic refresh for Live Video to Video") + go func() { + // 6 min to avoid Ticket Params Expired and to avoid getting TTL + refreshInterval := 6 * time.Minute + ticker := time.NewTicker(refreshInterval) + defer ticker.Stop() + for { + select { + case <-ticker.C: + ctx, cancel := context.WithTimeout(context.Background(), refreshInterval) + if err := sel.Refresh(ctx); err != nil { + clog.Infof(context.Background(), "Error refreshing AISessionSelector err=%v", err) + } + cancel() + } + } + }() +} + // newAICapabilities creates a new capabilities object with func newAICapabilities(cap core.Capability, modelID string, warm bool, minVersion string) *core.Capabilities { aiCaps := []core.Capability{cap} @@ -218,16 +266,35 @@ func newAICapabilities(cap core.Capability, modelID string, warm bool, minVersio return caps } +// SelectorIsEmpty returns true if no orchestrators are in the warm or cold pools. +func (sel *AISessionSelector) SelectorIsEmpty() bool { + return sel.warmPool.Size() == 0 && sel.coldPool.Size() == 0 +} + func (sel *AISessionSelector) Select(ctx context.Context) *AISession { shouldRefreshSelector := func() bool { + discoveryPoolSize := int(math.Min(float64(sel.node.OrchestratorPool.Size()), float64(sel.initialPoolSize))) + + // If the selector is empty, release all orchestrators from suspension and + // try refresh. + if sel.SelectorIsEmpty() { + clog.Infof(ctx, "refreshing sessions, no orchestrators in pools") + for i := 0; i < sel.penalty; i++ { + sel.suspender.signalRefresh() + } + } + // 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 + sel.warmPool.mu.Lock() + sel.coldPool.mu.Lock() + defer sel.coldPool.mu.Unlock() + defer sel.warmPool.mu.Unlock() if time.Now().After(sel.lastRefreshTime.Add(sel.ttl)) { return true } @@ -272,6 +339,10 @@ func (sel *AISessionSelector) Remove(sess *AISession) { } func (sel *AISessionSelector) Refresh(ctx context.Context) error { + // If we try to add new sessions to the pool the suspender + // should treat this as a refresh + sel.suspender.signalRefresh() + sessions, err := sel.getSessions(ctx) if err != nil { return err @@ -286,6 +357,13 @@ func (sel *AISessionSelector) Refresh(ctx context.Context) error { continue } + // We request 100 orchestrators in getSessions above so all Orchestrators are returned with refreshed information + // This keeps the suspended Orchestrators out of the pool until the selector is empty or 30 minutes has passed (refresh happens every 10 minutes) + if sel.suspender.Suspended(sess.Transcoder()) > 0 { + clog.V(common.DEBUG).Infof(ctx, "skipping suspended orchestrator=%s", sess.Transcoder()) + continue + } + // If the constraint for the modelID are missing skip this session modelConstraint, ok := constraints.Models[sel.modelID] if !ok { @@ -302,6 +380,11 @@ func (sel *AISessionSelector) Refresh(ctx context.Context) error { sel.warmPool.Add(warmSessions) sel.coldPool.Add(coldSessions) + sel.warmPool.mu.Lock() + sel.coldPool.mu.Lock() + defer sel.coldPool.mu.Unlock() + defer sel.warmPool.mu.Unlock() + sel.initialPoolSize = len(warmSessions) + len(coldSessions) + len(sel.suspender.list) sel.lastRefreshTime = time.Now() return nil @@ -357,6 +440,7 @@ func NewAISessionManager(node *core.LivepeerNode, ttl time.Duration) *AISessionM } func (c *AISessionManager) Select(ctx context.Context, cap core.Capability, modelID string) (*AISession, error) { + clog.V(common.DEBUG).Infof(ctx, "selecting orchestrator for modelID=%s", modelID) sel, err := c.getSelector(ctx, cap, modelID) if err != nil { return nil, err @@ -371,6 +455,8 @@ func (c *AISessionManager) Select(ctx context.Context, cap core.Capability, mode return nil, err } + clog.V(common.DEBUG).Infof(ctx, "selected orchestrator=%s", sess.Transcoder()) + return sess, nil } @@ -405,7 +491,7 @@ func (c *AISessionManager) getSelector(ctx context.Context, cap core.Capability, if !ok { // Create the selector var err error - sel, err = NewAISessionSelector(cap, modelID, c.node, c.ttl) + sel, err = NewAISessionSelector(ctx, cap, modelID, c.node, c.ttl) if err != nil { return nil, err } diff --git a/server/ai_worker.go b/server/ai_worker.go index 02ac6e2d1c..2b99a381d0 100644 --- a/server/ai_worker.go +++ b/server/ai_worker.go @@ -1,5 +1,7 @@ package server +// ai_worker.go implements logic for orchestrators to spin up and send AI jobs to workers. + import ( "bytes" "context" @@ -20,7 +22,7 @@ import ( "github.com/cenkalti/backoff" "github.com/golang/glog" - "github.com/livepeer/ai-worker/worker" + "github.com/livepeer/go-livepeer/ai/worker" "github.com/livepeer/go-livepeer/clog" "github.com/livepeer/go-livepeer/common" "github.com/livepeer/go-livepeer/core" diff --git a/server/ai_worker_test.go b/server/ai_worker_test.go index de169fbfe4..bfc82069ef 100644 --- a/server/ai_worker_test.go +++ b/server/ai_worker_test.go @@ -16,7 +16,7 @@ import ( "testing" "time" - "github.com/livepeer/ai-worker/worker" + "github.com/livepeer/go-livepeer/ai/worker" "github.com/livepeer/go-livepeer/common" "github.com/livepeer/go-livepeer/core" "github.com/livepeer/go-livepeer/net" @@ -609,7 +609,7 @@ func (a *stubAIWorker) TextToSpeech(ctx context.Context, req worker.GenTextToSpe } } -func (a *stubAIWorker) LiveVideoToVideo(ctx context.Context, req worker.GenLiveVideoToVideoJSONRequestBody) (*worker.LiveVideoToVideoResponse, error) { +func (a *stubAIWorker) LiveVideoToVideo(ctx context.Context, requestID, streamID string, req worker.GenLiveVideoToVideoJSONRequestBody) (*worker.LiveVideoToVideoResponse, error) { a.Called++ if a.Err != nil { return nil, a.Err diff --git a/server/auth.go b/server/auth.go index 400e29742a..a9105db0dd 100644 --- a/server/auth.go +++ b/server/auth.go @@ -109,8 +109,6 @@ type AIAuthRequest struct { // Gateway host GatewayHost string `json:"gateway_host"` - - // TODO not sure what params we need yet } // Contains the configuration parameters for this AI job diff --git a/server/broadcast.go b/server/broadcast.go index 9a2f515550..885ce2a1ad 100755 --- a/server/broadcast.go +++ b/server/broadcast.go @@ -920,6 +920,10 @@ func selectOrchestrator(ctx context.Context, n *core.LivepeerNode, params *core. if od.LocalInfo != nil { oScore = od.LocalInfo.Score } + var initialLatency time.Duration + if od.LocalInfo != nil && od.LocalInfo.Latency != nil { + initialLatency = *od.LocalInfo.Latency + } session := &BroadcastSession{ Broadcaster: core.NewBroadcaster(n), Params: params, @@ -934,6 +938,7 @@ func selectOrchestrator(ctx context.Context, n *core.LivepeerNode, params *core. lock: &sync.RWMutex{}, OrchestratorScore: oScore, InitialPrice: od.RemoteInfo.PriceInfo, + InitialLatency: initialLatency, } sessions = append(sessions, session) @@ -1474,7 +1479,7 @@ func verify(verifier *verification.SegmentVerifier, cxn *rtmpConnection, sess.lock.RUnlock() // Cache segment contents in params.Renditions // If we need to retry transcoding because verification fails, - // the the segments' OS location will be overwritten. + // the segments' OS location will be overwritten. // Cache the segments so we can restore them in OS if necessary. params := &verification.Params{ ManifestID: sess.Params.ManifestID, diff --git a/server/handlers.go b/server/handlers.go index 3f4673b6fe..823fc95f19 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -296,6 +296,88 @@ func getAvailableTranscodingOptionsHandler() http.Handler { }) } +// poolOrchestrator contains information about an orchestrator in a pool. +type poolOrchestrator struct { + Url string `json:"url"` + LatencyScore float64 `json:"latency_score"` + InFlight int `json:"in_flight"` +} + +// aiPoolInfo contains information about an AI pool. +type aiPoolInfo struct { + Size int `json:"size"` + InUse int `json:"in_use"` + Orchestrators []poolOrchestrator `json:"orchestrators"` +} + +// suspendedInfo contains information about suspended orchestrators. +type suspendedInfo struct { + List map[string]int `json:"list"` + CurrentCount int `json:"current_count"` +} + +// aiOrchestratorPools contains information about all AI pools. +type aiOrchestratorPools struct { + Cold aiPoolInfo `json:"cold"` + Warm aiPoolInfo `json:"warm"` + LastRefresh time.Time `json:"last_refresh"` + Suspended suspendedInfo `json:"suspended"` +} + +// getAIPoolsInfoHandler returns information about AI orchestrator pools. +func (s *LivepeerServer) getAIPoolsInfoHandler() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + aiPoolsInfoResp := make(map[string]aiOrchestratorPools) + + s.AISessionManager.mu.Lock() + defer s.AISessionManager.mu.Unlock() + + // Return if no selectors are present. + if len(s.AISessionManager.selectors) == 0 { + glog.Warning("Orchestrator pools are not yet initialized") + respondJson(w, aiPoolsInfoResp) + return + } + + // Loop through selectors and get pools info. + for cap, pool := range s.AISessionManager.selectors { + warmPool := aiPoolInfo{ + Size: pool.warmPool.Size(), + InUse: len(pool.warmPool.inUseSess), + } + for _, sess := range pool.warmPool.sessMap { + poolOrchestrator := poolOrchestrator{ + Url: sess.Transcoder(), + LatencyScore: sess.LatencyScore, + InFlight: len(sess.SegsInFlight), + } + warmPool.Orchestrators = append(warmPool.Orchestrators, poolOrchestrator) + } + + coldPool := aiPoolInfo{ + Size: pool.coldPool.Size(), + InUse: len(pool.coldPool.inUseSess), + } + for _, sess := range pool.coldPool.sessMap { + coldPool.Orchestrators = append(coldPool.Orchestrators, poolOrchestrator{ + Url: sess.Transcoder(), + LatencyScore: sess.LatencyScore, + InFlight: len(sess.SegsInFlight), + }) + } + + aiPoolsInfoResp[cap] = aiOrchestratorPools{ + Cold: coldPool, + Warm: warmPool, + LastRefresh: pool.lastRefreshTime, + Suspended: suspendedInfo{List: pool.suspender.list, CurrentCount: pool.suspender.count}, + } + } + + respondJson(w, aiPoolsInfoResp) + }) +} + // Rounds func currentRoundHandler(client eth.LivepeerEthClient) http.Handler { return mustHaveClient(client, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -1281,6 +1363,50 @@ func voteHandler(client eth.LivepeerEthClient) http.Handler { })) } +func proposalVoteHandler(client eth.LivepeerEthClient) http.Handler { + return mustHaveClient(client, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + proposalIDStr := r.FormValue("proposalID") + proposalID, ok := new(big.Int).SetString(proposalIDStr, 10) + if !ok { + respond500(w, "proposalID is not a valid integer value") + return + } + + supportStr := r.FormValue("support") + support, ok := new(big.Int).SetString(supportStr, 10) + if !ok { + respond500(w, "support is not a valid integer value") + return + } + if !types.ProposalVoteChoice(int(support.Int64())).IsValid() { + respond500(w, "invalid support") + return + } + + reason := r.FormValue("reason") + + // submit tx + var tx *ethtypes.Transaction + var err error + if reason != "" { + tx, err = client.ProposalVoteWithReason(proposalID, uint8(support.Uint64()), reason) + } else { + tx, err = client.ProposalVote(proposalID, uint8(support.Uint64())) + } + if err != nil { + respond500(w, fmt.Sprintf("unable to submit proposal vote transaction err=%q", err)) + return + } + + if err := client.CheckTx(tx); err != nil { + respond500(w, fmt.Sprintf("unable to mine proposal vote transaction err=%q", err)) + return + } + + respondOk(w, tx.Hash().Bytes()) + })) +} + // Gas Price func setMaxGasPriceHandler(client eth.LivepeerEthClient) http.Handler { return mustHaveClient(client, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/server/handlers_test.go b/server/handlers_test.go index 3a309395b9..63928e7ded 100644 --- a/server/handlers_test.go +++ b/server/handlers_test.go @@ -1217,8 +1217,93 @@ func TestVoteHandler(t *testing.T) { "choiceID": {"0"}, } handler = voteHandler(client) + status, _ = postForm(handler, form) + assert.Equal(http.StatusOK, status) +} + +func TestProposalVoteHandler(t *testing.T) { + assert := assert.New(t) + + client := ð.MockClient{StubClient: ð.StubClient{}} + tx := ethtypes.NewTx(ðtypes.LegacyTx{}) + + // Test missing client + handler := proposalVoteHandler(nil) + status, body := post(handler) + assert.Equal(http.StatusInternalServerError, status) + assert.Equal("missing ETH client", body) + + // Test invalid proposal ID + form := url.Values{ + "proposalID": {"foo"}, + "support": {"1"}, + } + handler = proposalVoteHandler(client) + status, body = postForm(handler, form) + assert.Equal(http.StatusInternalServerError, status) + assert.Equal("proposalID is not a valid integer value", body) + + // Test invalid support value (non-integer) + form = url.Values{ + "proposalID": {"1"}, + "support": {"foo"}, + } + handler = proposalVoteHandler(client) + status, body = postForm(handler, form) + assert.Equal(http.StatusInternalServerError, status) + assert.Equal("support is not a valid integer value", body) + + // Test invalid support value (out of range) + form = url.Values{ + "proposalID": {"1"}, + "support": {"3"}, + } + handler = proposalVoteHandler(client) status, body = postForm(handler, form) + assert.Equal(http.StatusInternalServerError, status) + assert.Equal("invalid support", body) + + // Test ProposalVote() error + form = url.Values{ + "proposalID": {"1"}, + "support": {"1"}, + } + err := errors.New("voting error") + client.On("ProposalVote", big.NewInt(1), uint8(1)).Return(nil, err).Once() + handler = proposalVoteHandler(client) + status, body = postForm(handler, form) + assert.Equal(http.StatusInternalServerError, status) + assert.Equal(fmt.Sprintf("unable to submit proposal vote transaction err=%q", err), body) + + // Test CheckTx() error + err = errors.New("unable to mine tx") + client.On("ProposalVote", big.NewInt(1), uint8(1)).Return(tx, nil).Once() + client.On("CheckTx", mock.Anything).Return(err).Once() + handler = proposalVoteHandler(client) + status, body = postForm(handler, form) + assert.Equal(http.StatusInternalServerError, status) + assert.Equal(fmt.Sprintf("unable to mine proposal vote transaction err=%q", err), body) + + // Test ProposalVote() success + client.On("ProposalVote", big.NewInt(1), uint8(1)).Return(tx, nil).Once() + client.On("CheckTx", mock.Anything).Return(nil).Once() + handler = proposalVoteHandler(client) + status, _ = postForm(handler, form) + assert.Equal(http.StatusOK, status) + client.AssertCalled(t, "ProposalVote", big.NewInt(1), uint8(1)) + + // Test ProposalVoteWithReason() success + form = url.Values{ + "proposalID": {"1"}, + "support": {"1"}, + "reason": {"Test reason"}, + } + client.On("ProposalVoteWithReason", big.NewInt(1), uint8(1), "Test reason").Return(tx, nil).Once() + client.On("CheckTx", mock.Anything).Return(nil).Once() + handler = proposalVoteHandler(client) + status, _ = postForm(handler, form) assert.Equal(http.StatusOK, status) + client.AssertCalled(t, "ProposalVoteWithReason", big.NewInt(1), uint8(1), "Test reason") } // Tickets diff --git a/server/rpc.go b/server/rpc.go index ff3c05e43c..a481b44200 100644 --- a/server/rpc.go +++ b/server/rpc.go @@ -16,7 +16,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials" - "github.com/livepeer/ai-worker/worker" + "github.com/livepeer/go-livepeer/ai/worker" "github.com/livepeer/go-livepeer/clog" "github.com/livepeer/go-livepeer/common" "github.com/livepeer/go-livepeer/core" @@ -78,7 +78,7 @@ type Orchestrator interface { SegmentAnything2(ctx context.Context, requestID string, req worker.GenSegmentAnything2MultipartRequestBody) (interface{}, error) ImageToText(ctx context.Context, requestID string, req worker.GenImageToTextMultipartRequestBody) (interface{}, error) TextToSpeech(ctx context.Context, requestID string, req worker.GenTextToSpeechJSONRequestBody) (interface{}, error) - LiveVideoToVideo(ctx context.Context, requestID string, req worker.GenLiveVideoToVideoJSONRequestBody) (interface{}, error) + LiveVideoToVideo(ctx context.Context, requestID, streamID string, req worker.GenLiveVideoToVideoJSONRequestBody) (interface{}, error) } // Balance describes methods for a session's balance maintenance @@ -139,6 +139,8 @@ type BroadcastSession struct { CleanupSession sessionsCleanup Balance Balance InitialPrice *net.PriceInfo + + InitialLatency time.Duration } func (bs *BroadcastSession) Transcoder() string { @@ -365,9 +367,32 @@ func getOrchestrator(orch Orchestrator, req *net.OrchestratorRequest) (*net.Orch return orchestratorInfo(orch, addr, orch.ServiceURI().String(), "") } + if err := checkLiveVideoToVideoCapacity(orch, req.Capabilities); err != nil { + return nil, fmt.Errorf("Invalid orchestrator request: %v", err) + } return orchestratorInfoWithCaps(orch, addr, orch.ServiceURI().String(), "", req.Capabilities) } +func checkLiveVideoToVideoCapacity(orch Orchestrator, caps *net.Capabilities) interface{} { + if caps.Constraints == nil || caps.Constraints.PerCapability == nil { + return nil + } + + if liveCap, ok := caps.Constraints.PerCapability[uint32(core.Capability_LiveVideoToVideo)]; ok { + pipeline := "live-video-to-video" + for modelID := range liveCap.GetModels() { + if orch.CheckAICapacity(pipeline, modelID) { + // It has capacity for at least one of the requested models + return nil + } + } + // No capacity for any requested model + return core.ErrOrchCap + } + // For no constraints or AI Jobs (non live-video-to-video), we don't want to check capacity + return nil +} + func endTranscodingSession(node *core.LivepeerNode, orch Orchestrator, req *net.EndTranscodingSessionRequest) (*net.EndTranscodingSessionResponse, error) { verifyToken := orch.AuthToken(req.AuthToken.SessionId, req.AuthToken.Expiration) if !bytes.Equal(verifyToken.Token, req.AuthToken.Token) { diff --git a/server/rpc_test.go b/server/rpc_test.go index fad0b63365..0b62a8cba6 100644 --- a/server/rpc_test.go +++ b/server/rpc_test.go @@ -25,7 +25,7 @@ import ( "github.com/golang/protobuf/proto" - "github.com/livepeer/ai-worker/worker" + "github.com/livepeer/go-livepeer/ai/worker" "github.com/livepeer/go-livepeer/common" "github.com/livepeer/go-livepeer/core" "github.com/livepeer/go-livepeer/crypto" @@ -223,7 +223,7 @@ func (r *stubOrchestrator) TextToSpeech(ctx context.Context, requestID string, r return nil, nil } -func (r *stubOrchestrator) LiveVideoToVideo(ctx context.Context, requestID string, req worker.GenLiveVideoToVideoJSONRequestBody) (interface{}, error) { +func (r *stubOrchestrator) LiveVideoToVideo(ctx context.Context, requestID, streamID string, req worker.GenLiveVideoToVideoJSONRequestBody) (interface{}, error) { return nil, nil } @@ -1429,7 +1429,7 @@ func (r *mockOrchestrator) ImageToText(ctx context.Context, requestID string, re func (r *mockOrchestrator) TextToSpeech(ctx context.Context, requestID string, req worker.GenTextToSpeechJSONRequestBody) (interface{}, error) { return nil, nil } -func (r *mockOrchestrator) LiveVideoToVideo(ctx context.Context, requestID string, req worker.GenLiveVideoToVideoJSONRequestBody) (interface{}, error) { +func (r *mockOrchestrator) LiveVideoToVideo(ctx context.Context, requestID, streamID string, req worker.GenLiveVideoToVideoJSONRequestBody) (interface{}, error) { return nil, nil } func (r *mockOrchestrator) CheckAICapacity(pipeline, modelID string) bool { diff --git a/server/selection.go b/server/selection.go index 9904d9a7b5..423ead100d 100644 --- a/server/selection.go +++ b/server/selection.go @@ -4,6 +4,7 @@ import ( "container/heap" "context" "math/big" + "sort" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/livepeer/go-livepeer/clog" @@ -89,19 +90,100 @@ func (r *storeStakeReader) Stakes(addrs []ethcommon.Address) (map[ethcommon.Addr return stakes, nil } -// MinLSSelector selects the next BroadcastSession with the lowest latency score if it is good enough. -// Otherwise, it selects a session that does not have a latency score yet -// MinLSSelector is not concurrency safe so the caller is responsible for ensuring safety for concurrent method calls -type MinLSSelector struct { - unknownSessions []*BroadcastSession - knownSessions *sessHeap +// Selector is the default selector which always selects the session with the lowest initial latency. +type Selector struct { + sessions []*BroadcastSession stakeRdr stakeReader selectionAlgorithm common.SelectionAlgorithm perfScore *common.PerfScore capabilities common.CapabilityComparator + sortCompFunc func(sess1, sess2 *BroadcastSession) bool +} + +func NewSelector(stakeRdr stakeReader, selectionAlgorithm common.SelectionAlgorithm, perfScore *common.PerfScore, capabilities common.CapabilityComparator) *Selector { + // By default, sort by initial latency + sortCompFunc := func(sess1, sess2 *BroadcastSession) bool { + return sess1.InitialLatency < sess2.InitialLatency + } + return &Selector{ + stakeRdr: stakeRdr, + selectionAlgorithm: selectionAlgorithm, + perfScore: perfScore, + capabilities: capabilities, + sortCompFunc: sortCompFunc, + } +} + +func NewSelectorOrderByLatencyScore(stakeRdr stakeReader, selectionAlgorithm common.SelectionAlgorithm, perfScore *common.PerfScore, capabilities common.CapabilityComparator) *Selector { + sortCompFunc := func(sess1, sess2 *BroadcastSession) bool { + return sess1.LatencyScore < sess2.LatencyScore + } + return &Selector{ + stakeRdr: stakeRdr, + selectionAlgorithm: selectionAlgorithm, + perfScore: perfScore, + capabilities: capabilities, + sortCompFunc: sortCompFunc, + } +} + +func (s *Selector) Add(sessions []*BroadcastSession) { + s.sessions = append(s.sessions, sessions...) + s.sort() +} + +func (s *Selector) Complete(sess *BroadcastSession) { + s.sessions = append(s.sessions, sess) + s.sort() +} - minLS float64 +func (s *Selector) sort() { + sort.Slice(s.sessions, func(i, j int) bool { + return s.sortCompFunc(s.sessions[i], s.sessions[j]) + }) +} + +func (s *Selector) Select(ctx context.Context) *BroadcastSession { + clog.V(common.DEBUG).Infof(ctx, "Selecting orchestrator") + availableOrchestrators := toOrchestrators(s.sessions) + sess := s.selectUnknownSession(ctx) + s.sort() + clog.V(common.DEBUG).Infof(ctx, "Selected orchestrator %s from available list: %v", toOrchestrator(sess), availableOrchestrators) + return sess +} + +func toOrchestrators(sessions []*BroadcastSession) []string { + orchestrators := make([]string, len(sessions)) + for i, sess := range sessions { + orchestrators[i] = toOrchestrator(sess) + } + return orchestrators +} + +func toOrchestrator(sess *BroadcastSession) string { + if sess != nil && sess.OrchestratorInfo != nil { + return sess.OrchestratorInfo.Transcoder + } + return "" +} + +func (s *Selector) Size() int { + return len(s.sessions) +} + +func (s *Selector) Clear() { + s.sessions = nil + s.stakeRdr = nil +} + +// MinLSSelector selects the next BroadcastSession with the lowest latency score if it is good enough. +// Otherwise, it selects a session that does not have a latency score yet +// MinLSSelector is not concurrency safe so the caller is responsible for ensuring safety for concurrent method calls +type MinLSSelector struct { + knownSessions *sessHeap + minLS float64 + Selector } // NewMinLSSelector returns an instance of MinLSSelector configured with a good enough latency score @@ -110,18 +192,20 @@ func NewMinLSSelector(stakeRdr stakeReader, minLS float64, selectionAlgorithm co heap.Init(knownSessions) return &MinLSSelector{ - knownSessions: knownSessions, - stakeRdr: stakeRdr, - selectionAlgorithm: selectionAlgorithm, - perfScore: perfScore, - capabilities: capabilities, - minLS: minLS, + knownSessions: knownSessions, + minLS: minLS, + Selector: Selector{ + stakeRdr: stakeRdr, + selectionAlgorithm: selectionAlgorithm, + perfScore: perfScore, + capabilities: capabilities, + }, } } // Add adds the sessions to the selector's list of sessions without a latency score func (s *MinLSSelector) Add(sessions []*BroadcastSession) { - s.unknownSessions = append(s.unknownSessions, sessions...) + s.sessions = append(s.sessions, sessions...) } // Complete adds the session to the selector's list sessions with a latency score @@ -138,7 +222,7 @@ func (s *MinLSSelector) Select(ctx context.Context) *BroadcastSession { } minSess := sess.(*BroadcastSession) - if minSess.LatencyScore > s.minLS && len(s.unknownSessions) > 0 { + if minSess.LatencyScore > s.minLS && len(s.sessions) > 0 { return s.selectUnknownSession(ctx) } @@ -147,33 +231,33 @@ func (s *MinLSSelector) Select(ctx context.Context) *BroadcastSession { // Size returns the number of sessions stored by the selector func (s *MinLSSelector) Size() int { - return len(s.unknownSessions) + s.knownSessions.Len() + return len(s.sessions) + s.knownSessions.Len() } // Clear resets the selector's state func (s *MinLSSelector) Clear() { - s.unknownSessions = nil + s.sessions = nil s.knownSessions = &sessHeap{} s.stakeRdr = nil } // Use selection algorithm to select from unknownSessions -func (s *MinLSSelector) selectUnknownSession(ctx context.Context) *BroadcastSession { - if len(s.unknownSessions) == 0 { +func (s *Selector) selectUnknownSession(ctx context.Context) *BroadcastSession { + if len(s.sessions) == 0 { return nil } if s.stakeRdr == nil { // Sessions are selected based on the order of unknownSessions in off-chain mode - sess := s.unknownSessions[0] - s.unknownSessions = s.unknownSessions[1:] + sess := s.sessions[0] + s.sessions = s.sessions[1:] return sess } var addrs []ethcommon.Address prices := map[ethcommon.Address]*big.Rat{} addrCount := make(map[ethcommon.Address]int) - for _, sess := range s.unknownSessions { + for _, sess := range s.sessions { if sess.OrchestratorInfo.GetTicketParams() == nil { continue } @@ -207,7 +291,7 @@ func (s *MinLSSelector) selectUnknownSession(ctx context.Context) *BroadcastSess selected := s.selectionAlgorithm.Select(ctx, addrs, stakes, maxPrice, prices, perfScores) - for i, sess := range s.unknownSessions { + for i, sess := range s.sessions { if sess.OrchestratorInfo.GetTicketParams() == nil { continue } @@ -221,10 +305,8 @@ func (s *MinLSSelector) selectUnknownSession(ctx context.Context) *BroadcastSess return nil } -func (s *MinLSSelector) removeUnknownSession(i int) { - n := len(s.unknownSessions) - s.unknownSessions[n-1], s.unknownSessions[i] = s.unknownSessions[i], s.unknownSessions[n-1] - s.unknownSessions = s.unknownSessions[:n-1] +func (s *Selector) removeUnknownSession(i int) { + s.sessions = append(s.sessions[:i], s.sessions[i+1:]...) } // LIFOSelector selects the next BroadcastSession in LIFO order diff --git a/server/selection_test.go b/server/selection_test.go index 1241875b27..57fae8acf9 100644 --- a/server/selection_test.go +++ b/server/selection_test.go @@ -6,6 +6,7 @@ import ( "errors" "math/big" "testing" + "time" "github.com/livepeer/go-livepeer/core" "github.com/livepeer/go-livepeer/net" @@ -157,6 +158,117 @@ func TestSessHeap(t *testing.T) { assert.Zero(h.Len()) } +func TestSelector_Select(t *testing.T) { + assert := assert.New(t) + + // given + sel := NewSelector(nil, stubSelectionAlgorithm{}, nil, nil) + sessions := []*BroadcastSession{ + {PMSessionID: "session-1", InitialLatency: 400 * time.Millisecond}, + {PMSessionID: "session-2", InitialLatency: 200 * time.Millisecond}, + {PMSessionID: "session-3", InitialLatency: 600 * time.Millisecond}, + } + sel.Add(sessions) + + // when + sess1 := sel.Select(context.Background()) + sess2 := sel.Select(context.Background()) + sess3 := sel.Select(context.Background()) + + // then + assert.Equal("session-2", sess1.PMSessionID) + assert.Equal("session-1", sess2.PMSessionID) + assert.Equal("session-3", sess3.PMSessionID) +} + +func TestSelector_CompleteAndSelect(t *testing.T) { + assert := assert.New(t) + + // given + sel := NewSelector(nil, stubSelectionAlgorithm{}, nil, nil) + sessions := []*BroadcastSession{ + {PMSessionID: "session-1", InitialLatency: 400 * time.Millisecond}, + {PMSessionID: "session-2", InitialLatency: 200 * time.Millisecond}, + {PMSessionID: "session-3", InitialLatency: 600 * time.Millisecond}, + } + sel.Add(sessions) + + // when + sess1 := sel.Select(context.Background()) + sel.Complete(sess1) + sess2 := sel.Select(context.Background()) + sess3 := sel.Select(context.Background()) + sel.Complete(sess3) + sel.Complete(sess2) + sess4 := sel.Select(context.Background()) + + // then + assert.Equal("session-2", sess1.PMSessionID) + assert.Equal("session-2", sess2.PMSessionID) + assert.Equal("session-1", sess3.PMSessionID) + assert.Equal("session-2", sess4.PMSessionID) +} + +func TestSelector_Size(t *testing.T) { + assert := assert.New(t) + + // given + sel := NewSelector(nil, stubSelectionAlgorithm{}, nil, nil) + sessions := []*BroadcastSession{ + {PMSessionID: "session-1", InitialLatency: 400 * time.Millisecond}, + {PMSessionID: "session-2", InitialLatency: 200 * time.Millisecond}, + {PMSessionID: "session-3", InitialLatency: 600 * time.Millisecond}, + } + sel.Add(sessions) + + // when & then + assert.Equal(3, sel.Size()) + sess1 := sel.Select(context.Background()) + assert.Equal(2, sel.Size()) + sel.Complete(sess1) + assert.Equal(3, sel.Size()) + sess2 := sel.Select(context.Background()) + sess3 := sel.Select(context.Background()) + assert.Equal(1, sel.Size()) + sel.Complete(sess3) + sel.Complete(sess2) + assert.Equal(3, sel.Size()) + sel.Clear() + assert.Equal(0, sel.Size()) + assert.Nil(sel.Select(context.Background())) +} + +func TestSelector_SortByInitialLatency(t *testing.T) { + assert := assert.New(t) + + sel := NewSelector(nil, stubSelectionAlgorithm{}, nil, nil) + sessions := []*BroadcastSession{ + {PMSessionID: "session-1", InitialLatency: 400 * time.Millisecond}, + {PMSessionID: "session-2", InitialLatency: 200 * time.Millisecond}, + {PMSessionID: "session-3", InitialLatency: 600 * time.Millisecond}, + } + sel.Add(sessions) + + assert.Equal("session-2", sel.sessions[0].PMSessionID) + assert.Equal("session-1", sel.sessions[1].PMSessionID) + assert.Equal("session-3", sel.sessions[2].PMSessionID) +} + +func TestSelector_SortByLatencyScore(t *testing.T) { + assert := assert.New(t) + + sel := NewSelectorOrderByLatencyScore(nil, stubSelectionAlgorithm{}, nil, nil) + sessions := []*BroadcastSession{ + {PMSessionID: "session-1", InitialLatency: 400 * time.Millisecond, LatencyScore: 0.001}, + {PMSessionID: "session-2", InitialLatency: 200 * time.Millisecond, LatencyScore: 0.01}, + {PMSessionID: "session-3", InitialLatency: 600 * time.Millisecond, LatencyScore: 0.08}, + } + sel.Add(sessions) + assert.Equal("session-1", sel.sessions[0].PMSessionID) + assert.Equal("session-2", sel.sessions[1].PMSessionID) + assert.Equal("session-3", sel.sessions[2].PMSessionID) +} + func TestMinLSSelector(t *testing.T) { assert := assert.New(t) @@ -175,60 +287,60 @@ func TestMinLSSelector(t *testing.T) { sel.Add(sessions) assert.Equal(sel.Size(), 3) for _, sess := range sessions { - assert.Contains(sel.unknownSessions, sess) + assert.Contains(sel.sessions, sess) } - // Select from unknownSessions + // Select from sessions sess1 := sel.Select(context.TODO()) assert.Equal(sel.Size(), 2) - assert.Equal(len(sel.unknownSessions), 2) + assert.Equal(len(sel.sessions), 2) // Set sess1.LatencyScore to not be good enough sess1.LatencyScore = 1.1 sel.Complete(sess1) assert.Equal(sel.Size(), 3) - assert.Equal(len(sel.unknownSessions), 2) + assert.Equal(len(sel.sessions), 2) assert.Equal(sel.knownSessions.Len(), 1) - // Select from unknownSessions + // Select from sessions sess2 := sel.Select(context.TODO()) assert.Equal(sel.Size(), 2) - assert.Equal(len(sel.unknownSessions), 1) + assert.Equal(len(sel.sessions), 1) assert.Equal(sel.knownSessions.Len(), 1) // Set sess2.LatencyScore to be good enough sess2.LatencyScore = .9 sel.Complete(sess2) assert.Equal(sel.Size(), 3) - assert.Equal(len(sel.unknownSessions), 1) + assert.Equal(len(sel.sessions), 1) assert.Equal(sel.knownSessions.Len(), 2) // Select from knownSessions knownSess := sel.Select(context.TODO()) assert.Equal(sel.Size(), 2) - assert.Equal(len(sel.unknownSessions), 1) + assert.Equal(len(sel.sessions), 1) assert.Equal(sel.knownSessions.Len(), 1) assert.Equal(knownSess, sess2) // Set knownSess.LatencyScore to not be good enough knownSess.LatencyScore = 1.1 sel.Complete(knownSess) - // Clear unknownSessions + // Clear sessions sess := sel.Select(context.TODO()) sess.LatencyScore = 2.1 sel.Complete(sess) - assert.Equal(len(sel.unknownSessions), 0) + assert.Equal(len(sel.sessions), 0) assert.Equal(sel.knownSessions.Len(), 3) // Select from knownSessions knownSess = sel.Select(context.TODO()) assert.Equal(sel.Size(), 2) - assert.Equal(len(sel.unknownSessions), 0) + assert.Equal(len(sel.sessions), 0) assert.Equal(sel.knownSessions.Len(), 2) sel.Clear() assert.Zero(sel.Size()) - assert.Nil(sel.unknownSessions) + assert.Nil(sel.sessions) assert.Zero(sel.knownSessions.Len()) assert.Nil(sel.stakeRdr) } @@ -245,56 +357,56 @@ func TestMinLSSelector_RemoveUnknownSession(t *testing.T) { {Params: &core.StreamParameters{ManifestID: "baz"}}, } - resetUnknownSessions := func() { - // Make a copy of the original slice so we can reset unknownSessions to the original slice - sel.unknownSessions = make([]*BroadcastSession, len(sessions)) - copy(sel.unknownSessions, sessions) + resetsessions := func() { + // Make a copy of the original slice so we can reset sessions to the original slice + sel.sessions = make([]*BroadcastSession, len(sessions)) + copy(sel.sessions, sessions) } // Test remove from front of list - resetUnknownSessions() + resetsessions() sel.removeUnknownSession(0) - assert.Len(sel.unknownSessions, 2) - assert.Equal("baz", string(sel.unknownSessions[0].Params.ManifestID)) - assert.Equal("bar", string(sel.unknownSessions[1].Params.ManifestID)) + assert.Len(sel.sessions, 2) + assert.Equal("bar", string(sel.sessions[0].Params.ManifestID)) + assert.Equal("baz", string(sel.sessions[1].Params.ManifestID)) // Test remove from middle of list - resetUnknownSessions() + resetsessions() sel.removeUnknownSession(1) - assert.Len(sel.unknownSessions, 2) - assert.Equal("foo", string(sel.unknownSessions[0].Params.ManifestID)) - assert.Equal("baz", string(sel.unknownSessions[1].Params.ManifestID)) + assert.Len(sel.sessions, 2) + assert.Equal("foo", string(sel.sessions[0].Params.ManifestID)) + assert.Equal("baz", string(sel.sessions[1].Params.ManifestID)) // Test remove from back of list - resetUnknownSessions() + resetsessions() sel.removeUnknownSession(2) - assert.Len(sel.unknownSessions, 2) - assert.Equal("foo", string(sel.unknownSessions[0].Params.ManifestID)) - assert.Equal("bar", string(sel.unknownSessions[1].Params.ManifestID)) + assert.Len(sel.sessions, 2) + assert.Equal("foo", string(sel.sessions[0].Params.ManifestID)) + assert.Equal("bar", string(sel.sessions[1].Params.ManifestID)) // Test remove when list length = 1 - sel.unknownSessions = []*BroadcastSession{{}} + sel.sessions = []*BroadcastSession{{}} sel.removeUnknownSession(0) - assert.Empty(sel.unknownSessions) + assert.Empty(sel.sessions) } func TestMinLSSelector_SelectUnknownSession(t *testing.T) { tests := []struct { - name string - unknownSessions []*BroadcastSession - stakes map[ethcommon.Address]int64 - perfScores map[ethcommon.Address]float64 - want *BroadcastSession + name string + sessions []*BroadcastSession + stakes map[ethcommon.Address]int64 + perfScores map[ethcommon.Address]float64 + want *BroadcastSession }{ { - name: "No unknown sessions", - unknownSessions: []*BroadcastSession{}, - want: nil, + name: "No unknown sessions", + sessions: []*BroadcastSession{}, + want: nil, }, { name: "Select lowest price", - unknownSessions: []*BroadcastSession{ + sessions: []*BroadcastSession{ sessionWithPrice("0x0000000000000000000000000000000000000001", 1000, 1), sessionWithPrice("0x0000000000000000000000000000000000000002", 500, 1), }, @@ -302,7 +414,7 @@ func TestMinLSSelector_SelectUnknownSession(t *testing.T) { }, { name: "Select highest stake", - unknownSessions: []*BroadcastSession{ + sessions: []*BroadcastSession{ session("0x0000000000000000000000000000000000000001"), session("0x0000000000000000000000000000000000000002"), }, @@ -314,7 +426,7 @@ func TestMinLSSelector_SelectUnknownSession(t *testing.T) { }, { name: "Select highest performance score", - unknownSessions: []*BroadcastSession{ + sessions: []*BroadcastSession{ session("0x0000000000000000000000000000000000000001"), session("0x0000000000000000000000000000000000000002"), }, @@ -338,7 +450,7 @@ func TestMinLSSelector_SelectUnknownSession(t *testing.T) { perfScore = &common.PerfScore{Scores: tt.perfScores} } sel := NewMinLSSelector(stakeRdr, 1.0, selAlg, perfScore, nil) - sel.Add(tt.unknownSessions) + sel.Add(tt.sessions) sess := sel.selectUnknownSession(context.TODO()) @@ -378,8 +490,8 @@ func TestMinLSSelector_SelectUnknownSession_NilStakeReader(t *testing.T) { sel.Add(sessions) i := 0 - // Check that we select sessions based on the order of unknownSessions and that the size of - // unknownSessions decreases with each selection + // Check that we select sessions based on the order of sessions and that the size of + // sessions decreases with each selection for sel.Size() > 0 { sess := sel.selectUnknownSession(context.TODO()) assert.Same(t, sess, sessions[i]) diff --git a/server/webserver.go b/server/webserver.go index cf1578b317..5b6def988d 100644 --- a/server/webserver.go +++ b/server/webserver.go @@ -50,6 +50,7 @@ func (s *LivepeerServer) cliWebServerHandlers(bindAddr string) *http.ServeMux { mux.Handle("/getBroadcastConfig", getBroadcastConfigHandler()) mux.Handle("/getAvailableTranscodingOptions", getAvailableTranscodingOptionsHandler()) mux.Handle("/setMaxPriceForCapability", mustHaveFormParams(s.setMaxPriceForCapability(), "maxPricePerUnit", "pixelsPerUnit", "currency", "pipeline", "modelID")) + mux.Handle("/getAISessionPoolsInfo", s.getAIPoolsInfoHandler()) // Rounds mux.Handle("/currentRound", currentRoundHandler(client)) @@ -88,6 +89,7 @@ func (s *LivepeerServer) cliWebServerHandlers(bindAddr string) *http.ServeMux { mux.Handle("/requestTokens", requestTokensHandler(client)) mux.Handle("/signMessage", mustHaveFormParams(signMessageHandler(client), "message")) mux.Handle("/vote", mustHaveFormParams(voteHandler(client), "poll", "choiceID")) + mux.Handle("/voteOnProposal", mustHaveFormParams(proposalVoteHandler(client), "proposalID", "support")) // Gas Price mux.Handle("/setMaxGasPrice", mustHaveFormParams(setMaxGasPriceHandler(client), "amount")) diff --git a/test.sh b/test.sh index 4f45d7abf9..dee1543eff 100755 --- a/test.sh +++ b/test.sh @@ -24,6 +24,10 @@ go test -run TestSelectSession_ -race go test -run RegisterConnection -race cd .. +cd media +go test -race +cd .. + ./test_args.sh printf "\n\nAll Tests Passed\n\n"