diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index bd4caf70d2..cd86a5c596 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -4,6 +4,7 @@ - [#3055](https://github.com/livepeer/go-livepeer/pull/3055) census: Rename broadcaster metrics to gateway metrics - [#3053](https://github.com/livepeer/go-livepeer/pull/3053) cli: add `-gateway` flag and deprecate `-broadcaster` flag. +- [#3056](https://github.com/livepeer/go-livepeer/pull/3056) cli: add `-pricePerGateway` flag and deprecate `-pricePerBroadcaster` flag. ### Breaking Changes 🚨🚨 diff --git a/cmd/livepeer/livepeer.go b/cmd/livepeer/livepeer.go index 0568b70836..0bf119b7bb 100755 --- a/cmd/livepeer/livepeer.go +++ b/cmd/livepeer/livepeer.go @@ -179,6 +179,7 @@ func parseLivepeerConfig() starter.LivepeerConfig { cfg.PixelsPerUnit = flag.String("pixelsPerUnit", *cfg.PixelsPerUnit, "Amount of pixels per unit. Set to '> 1' to have smaller price granularity than 1 wei / pixel") cfg.PriceFeedAddr = flag.String("priceFeedAddr", *cfg.PriceFeedAddr, "ETH address of the Chainlink price feed contract. Used for custom currencies conversion on -pricePerUnit or -maxPricePerUnit") cfg.AutoAdjustPrice = flag.Bool("autoAdjustPrice", *cfg.AutoAdjustPrice, "Enable/disable automatic price adjustments based on the overhead for redeeming tickets") + cfg.PricePerGateway = flag.String("pricePerGateway", *cfg.PricePerGateway, `json list of price per gateway or path to json config file. Example: {"broadcasters":[{"ethaddress":"address1","priceperunit":0.5,"currency":"USD","pixelsperunit":1000000000000},{"ethaddress":"address2","priceperunit":0.3,"currency":"USD","pixelsperunit":1000000000000}]}`) cfg.PricePerBroadcaster = flag.String("pricePerBroadcaster", *cfg.PricePerBroadcaster, `json list of price per broadcaster or path to json config file. Example: {"broadcasters":[{"ethaddress":"address1","priceperunit":0.5,"currency":"USD","pixelsperunit":1000000000000},{"ethaddress":"address2","priceperunit":0.3,"currency":"USD","pixelsperunit":1000000000000}]}`) // Interval to poll for blocks cfg.BlockPollingInterval = flag.Int("blockPollingInterval", *cfg.BlockPollingInterval, "Interval in seconds at which different blockchain event services poll for blocks") diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go index 3ed6edfaa0..1053506bda 100755 --- a/cmd/livepeer/starter/starter.go +++ b/cmd/livepeer/starter/starter.go @@ -126,6 +126,7 @@ type LivepeerConfig struct { PixelsPerUnit *string PriceFeedAddr *string AutoAdjustPrice *bool + PricePerGateway *string PricePerBroadcaster *string BlockPollingInterval *int Redeemer *bool @@ -204,6 +205,7 @@ func DefaultLivepeerConfig() LivepeerConfig { defaultPixelsPerUnit := "1" defaultPriceFeedAddr := "0x639Fe6ab55C921f74e7fac1ee960C0B6293ba612" // ETH / USD price feed address on Arbitrum Mainnet defaultAutoAdjustPrice := true + defaultPricePerGateway := "" defaultPricePerBroadcaster := "" defaultBlockPollingInterval := 5 defaultRedeemer := false @@ -292,6 +294,7 @@ func DefaultLivepeerConfig() LivepeerConfig { PixelsPerUnit: &defaultPixelsPerUnit, PriceFeedAddr: &defaultPriceFeedAddr, AutoAdjustPrice: &defaultAutoAdjustPrice, + PricePerGateway: &defaultPricePerGateway, PricePerBroadcaster: &defaultPricePerBroadcaster, BlockPollingInterval: &defaultBlockPollingInterval, Redeemer: &defaultRedeemer, @@ -784,7 +787,11 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) { } n.SetBasePrice("default", autoPrice) - broadcasterPrices := getBroadcasterPrices(*cfg.PricePerBroadcaster) + if *cfg.PricePerBroadcaster != "" { + glog.Warning("-PricePerBroadcaster flag is deprecated and will be removed in a future release. Please use -PricePerGateway instead") + cfg.PricePerGateway = cfg.PricePerBroadcaster + } + broadcasterPrices := getGatewayPrices(*cfg.PricePerGateway) for _, p := range broadcasterPrices { p := p pricePerPixel := new(big.Rat).Quo(p.PricePerUnit, p.PixelsPerUnit) @@ -1482,51 +1489,64 @@ func checkOrStoreChainID(dbh *common.DB, chainID *big.Int) error { return nil } -type BroadcasterPrice struct { +type GatewayPrice struct { EthAddress string PricePerUnit *big.Rat Currency string PixelsPerUnit *big.Rat } -func getBroadcasterPrices(broadcasterPrices string) []BroadcasterPrice { - if broadcasterPrices == "" { +func getGatewayPrices(gatewayPrices string) []GatewayPrice { + if gatewayPrices == "" { return nil } // Format of broadcasterPrices json - // {"broadcasters":[{"ethaddress":"address1","priceperunit":0.5,"currency":"USD","pixelsperunit":1}, {"ethaddress":"address2","priceperunit":0.3,"currency":"USD","pixelsperunit":3}]} + // {"gateways":[{"ethaddress":"address1","priceperunit":0.5,"currency":"USD","pixelsperunit":1}, {"ethaddress":"address2","priceperunit":0.3,"currency":"USD","pixelsperunit":3}]} var pricesSet struct { + Gateways []struct { + EthAddress string `json:"ethaddress"` + PixelsPerUnit json.RawMessage `json:"pixelsperunit"` + PricePerUnit json.RawMessage `json:"priceperunit"` + Currency string `json:"currency"` + } `json:"gateways"` + // TODO: Keep the old name for backwards compatibility, remove in the future Broadcasters []struct { - EthAddress string `json:"ethaddress"` - // The fields below are specified as a number in the JSON, but we don't want to lose precision so we store the raw characters here and parse as a big.Rat. - // This also allows support for exponential notation for numbers, which is helpful for pricePerUnit which could be a value like 1e12. + EthAddress string `json:"ethaddress"` PixelsPerUnit json.RawMessage `json:"pixelsperunit"` PricePerUnit json.RawMessage `json:"priceperunit"` Currency string `json:"currency"` } `json:"broadcasters"` } - pricesFileContent, _ := common.ReadFromFile(broadcasterPrices) + pricesFileContent, _ := common.ReadFromFile(gatewayPrices) err := json.Unmarshal([]byte(pricesFileContent), &pricesSet) if err != nil { - glog.Errorf("broadcaster prices could not be parsed: %s", err) + glog.Errorf("gateway prices could not be parsed: %s", err) return nil } - prices := make([]BroadcasterPrice, len(pricesSet.Broadcasters)) - for i, p := range pricesSet.Broadcasters { + // Check if broadcasters field is used and display a warning + if len(pricesSet.Broadcasters) > 0 { + glog.Warning("The 'broadcaster' property in the 'pricePerGateway' config is deprecated and will be removed in a future release. Please use 'gateways' instead.") + } + + // Combine broadcasters and gateways into a single slice + allGateways := append(pricesSet.Broadcasters, pricesSet.Gateways...) + + prices := make([]GatewayPrice, len(allGateways)) + for i, p := range allGateways { pixelsPerUnit, ok := new(big.Rat).SetString(string(p.PixelsPerUnit)) if !ok { - glog.Errorf("Pixels per unit could not be parsed for broadcaster %v. must be a valid number, provided %s", p.EthAddress, p.PixelsPerUnit) + glog.Errorf("Pixels per unit could not be parsed for gateway %v. must be a valid number, provided %s", p.EthAddress, p.PixelsPerUnit) continue } pricePerUnit, ok := new(big.Rat).SetString(string(p.PricePerUnit)) if !ok { - glog.Errorf("Price per unit could not be parsed for broadcaster %v. must be a valid number, provided %s", p.EthAddress, p.PricePerUnit) + glog.Errorf("Price per unit could not be parsed for gateway %v. must be a valid number, provided %s", p.EthAddress, p.PricePerUnit) continue } - prices[i] = BroadcasterPrice{ + prices[i] = GatewayPrice{ EthAddress: p.EthAddress, Currency: p.Currency, PricePerUnit: pricePerUnit, diff --git a/cmd/livepeer/starter/starter_test.go b/cmd/livepeer/starter/starter_test.go index 45d3620b8e..60df927897 100644 --- a/cmd/livepeer/starter/starter_test.go +++ b/cmd/livepeer/starter/starter_test.go @@ -2,6 +2,7 @@ package starter import ( "errors" + "fmt" "math/big" "os" "path/filepath" @@ -87,19 +88,25 @@ func TestIsLocalURL(t *testing.T) { assert.False(isLocal) } -func TestParseGetBroadcasterPrices(t *testing.T) { +func TestParseGetGatewayPrices(t *testing.T) { assert := assert.New(t) - j := `{"broadcasters":[{"ethaddress":"0x0000000000000000000000000000000000000000","priceperunit":1000,"pixelsperunit":1}, {"ethaddress":"0x1000000000000000000000000000000000000000","priceperunit":2000,"pixelsperunit":3}]}` + // TODO: Keep checking old field for backwards compatibility, remove in future + jsonTemplate := `{"%s":[{"ethaddress":"0x0000000000000000000000000000000000000000","priceperunit":1000,"pixelsperunit":1}, {"ethaddress":"0x1000000000000000000000000000000000000000","priceperunit":2000,"pixelsperunit":3}]}` + testCases := []string{"gateways", "broadcasters"} - prices := getBroadcasterPrices(j) - assert.NotNil(prices) - assert.Equal(2, len(prices)) + for _, tc := range testCases { + jsonStr := fmt.Sprintf(jsonTemplate, tc) - price1 := new(big.Rat).Quo(prices[0].PricePerUnit, prices[0].PixelsPerUnit) - price2 := new(big.Rat).Quo(prices[1].PricePerUnit, prices[1].PixelsPerUnit) - assert.Equal(big.NewRat(1000, 1), price1) - assert.Equal(big.NewRat(2000, 3), price2) + prices := getGatewayPrices(jsonStr) + assert.NotNil(prices) + assert.Equal(2, len(prices)) + + price1 := new(big.Rat).Quo(prices[0].PricePerUnit, prices[0].PixelsPerUnit) + price2 := new(big.Rat).Quo(prices[1].PricePerUnit, prices[1].PixelsPerUnit) + assert.Equal(big.NewRat(1000, 1), price1) + assert.Equal(big.NewRat(2000, 3), price2) + } } // Address provided to keystore file