From 718ed0ca518e11ec63aebfa5fd68f4b3d752b3aa Mon Sep 17 00:00:00 2001 From: Kay Date: Mon, 1 Jul 2024 19:33:33 +0000 Subject: [PATCH 1/7] feat(voucher): create command --- go.mod | 22 +- go.sum | 21 +- .../engine/command/calculator/calculator.go | 9 +- internal/engine/command/command.go | 11 +- internal/engine/command/market/market.go | 6 +- internal/engine/command/market/price_test.go | 3 +- internal/engine/command/network/network.go | 12 +- internal/engine/command/phoenix/faucet.go | 2 +- .../engine/command/phoenix/faucet_test.go | 1 - internal/engine/command/phoenix/phoenix.go | 9 +- internal/engine/command/voucher/claim.go | 6 + internal/engine/command/voucher/create.go | 49 + internal/engine/command/voucher/voucher.go | 54 +- internal/engine/command/zealy/zealy.go | 9 +- internal/engine/engine.go | 3 +- internal/entity/voucher.go | 15 +- internal/platforms/discord/discord.go | 10 +- internal/repository/voucher.go | 11 + vendor/filippo.io/edwards25519/LICENSE | 27 + vendor/filippo.io/edwards25519/README.md | 14 + vendor/filippo.io/edwards25519/doc.go | 20 + .../filippo.io/edwards25519/edwards25519.go | 427 ++++++ vendor/filippo.io/edwards25519/extra.go | 349 +++++ vendor/filippo.io/edwards25519/field/fe.go | 420 ++++++ .../filippo.io/edwards25519/field/fe_amd64.go | 16 + .../filippo.io/edwards25519/field/fe_amd64.s | 379 ++++++ .../edwards25519/field/fe_amd64_noasm.go | 12 + .../filippo.io/edwards25519/field/fe_arm64.go | 16 + .../filippo.io/edwards25519/field/fe_arm64.s | 42 + .../edwards25519/field/fe_arm64_noasm.go | 12 + .../filippo.io/edwards25519/field/fe_extra.go | 50 + .../edwards25519/field/fe_generic.go | 266 ++++ vendor/filippo.io/edwards25519/scalar.go | 343 +++++ vendor/filippo.io/edwards25519/scalar_fiat.go | 1147 +++++++++++++++++ vendor/filippo.io/edwards25519/scalarmult.go | 214 +++ vendor/filippo.io/edwards25519/tables.go | 129 ++ .../gotgbot/v2/custom_helpers.go | 18 + .../PaulSonOfLars/gotgbot/v2/gen_helpers.go | 2 +- .../PaulSonOfLars/gotgbot/v2/gen_methods.go | 219 ++-- .../PaulSonOfLars/gotgbot/v2/gen_types.go | 1145 ++++++++++++---- .../PaulSonOfLars/gotgbot/v2/spec_commit | 2 +- vendor/github.com/go-sql-driver/mysql/AUTHORS | 18 + .../go-sql-driver/mysql/CHANGELOG.md | 61 +- .../github.com/go-sql-driver/mysql/README.md | 82 +- vendor/github.com/go-sql-driver/mysql/auth.go | 63 +- .../go-sql-driver/mysql/collations.go | 2 +- .../go-sql-driver/mysql/connection.go | 104 +- .../go-sql-driver/mysql/connector.go | 67 +- .../github.com/go-sql-driver/mysql/const.go | 15 +- .../github.com/go-sql-driver/mysql/driver.go | 27 +- vendor/github.com/go-sql-driver/mysql/dsn.go | 128 +- .../github.com/go-sql-driver/mysql/errors.go | 18 +- .../github.com/go-sql-driver/mysql/fields.go | 70 +- vendor/github.com/go-sql-driver/mysql/fuzz.go | 25 - .../github.com/go-sql-driver/mysql/infile.go | 12 +- .../go-sql-driver/mysql/nulltime.go | 4 +- .../github.com/go-sql-driver/mysql/packets.go | 209 +-- .../github.com/go-sql-driver/mysql/result.go | 36 +- vendor/github.com/go-sql-driver/mysql/rows.go | 13 +- .../go-sql-driver/mysql/statement.go | 23 +- .../github.com/go-sql-driver/mysql/utils.go | 13 +- vendor/gorm.io/driver/mysql/migrator.go | 10 +- vendor/gorm.io/driver/mysql/mysql.go | 6 +- vendor/modules.txt | 16 +- 64 files changed, 5847 insertions(+), 697 deletions(-) create mode 100644 internal/engine/command/voucher/create.go create mode 100644 vendor/filippo.io/edwards25519/LICENSE create mode 100644 vendor/filippo.io/edwards25519/README.md create mode 100644 vendor/filippo.io/edwards25519/doc.go create mode 100644 vendor/filippo.io/edwards25519/edwards25519.go create mode 100644 vendor/filippo.io/edwards25519/extra.go create mode 100644 vendor/filippo.io/edwards25519/field/fe.go create mode 100644 vendor/filippo.io/edwards25519/field/fe_amd64.go create mode 100644 vendor/filippo.io/edwards25519/field/fe_amd64.s create mode 100644 vendor/filippo.io/edwards25519/field/fe_amd64_noasm.go create mode 100644 vendor/filippo.io/edwards25519/field/fe_arm64.go create mode 100644 vendor/filippo.io/edwards25519/field/fe_arm64.s create mode 100644 vendor/filippo.io/edwards25519/field/fe_arm64_noasm.go create mode 100644 vendor/filippo.io/edwards25519/field/fe_extra.go create mode 100644 vendor/filippo.io/edwards25519/field/fe_generic.go create mode 100644 vendor/filippo.io/edwards25519/scalar.go create mode 100644 vendor/filippo.io/edwards25519/scalar_fiat.go create mode 100644 vendor/filippo.io/edwards25519/scalarmult.go create mode 100644 vendor/filippo.io/edwards25519/tables.go delete mode 100644 vendor/github.com/go-sql-driver/mysql/fuzz.go diff --git a/go.mod b/go.mod index 8876900e..0363e151 100644 --- a/go.mod +++ b/go.mod @@ -3,34 +3,30 @@ module github.com/pagu-project/Pagu go 1.22.2 require ( - github.com/PaulSonOfLars/gotgbot/v2 v2.0.0-rc.26 + github.com/PaulSonOfLars/gotgbot/v2 v2.0.0-rc.27 github.com/bwmarrin/discordgo v0.28.1 github.com/labstack/echo/v4 v4.12.0 github.com/pactus-project/pactus v1.3.0 github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.9.0 google.golang.org/grpc v1.64.0 - gorm.io/driver/mysql v1.5.6 + gorm.io/driver/mysql v1.5.7 gorm.io/gorm v1.25.10 ) require ( - github.com/go-sql-driver/mysql v1.7.0 // indirect - github.com/google/pprof v0.0.0-20240622144329-c177fd99eaa9 // indirect - github.com/ipfs/boxo v0.21.0 // indirect - github.com/prometheus/common v0.54.0 // indirect - github.com/quic-go/quic-go v0.45.1 // indirect -) - -require ( + filippo.io/edwards25519 v1.1.0 // indirect github.com/NathanBaulch/protoc-gen-cobra v1.2.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/go-sql-driver/mysql v1.8.1 // indirect + github.com/google/pprof v0.0.0-20240622144329-c177fd99eaa9 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/iancoleman/strcase v0.3.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/ipfs/boxo v0.21.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/kilic/bls12-381 v0.1.0 // indirect @@ -40,6 +36,8 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/common v0.54.0 // indirect + github.com/quic-go/quic-go v0.45.1 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/rs/zerolog v1.33.0 github.com/spf13/cast v1.6.0 // indirect @@ -54,8 +52,8 @@ require ( golang.org/x/net v0.26.0 // indirect golang.org/x/sys v0.21.0 // indirect golang.org/x/text v0.16.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect google.golang.org/protobuf v1.34.2 gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/yaml.v3 v3.0.1 diff --git a/go.sum b/go.sum index 3617b108..f5b1c422 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,9 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/NathanBaulch/protoc-gen-cobra v1.2.1 h1:BOqX9glwicbqDJDGndMnhHhx8psGTSjGdZzRDY1a7A8= github.com/NathanBaulch/protoc-gen-cobra v1.2.1/go.mod h1:ZLPLEPQgV3jP3a7IEp+xxYPk8tF4lhY9ViV0hn6K3iA= -github.com/PaulSonOfLars/gotgbot/v2 v2.0.0-rc.26 h1:u1ZGYo3ml5ouOI9rCIknF0JO9REeKb69E6drCwZgZdc= -github.com/PaulSonOfLars/gotgbot/v2 v2.0.0-rc.26/go.mod h1:kL1v4iIjlalwm3gCYGvF4NLa3hs+aKEfRkNJvj4aoDU= +github.com/PaulSonOfLars/gotgbot/v2 v2.0.0-rc.27 h1:rOlGzmYC3jPVPLVLWKMiiYuePQ6MV8Cyw5qJYBoMnkY= +github.com/PaulSonOfLars/gotgbot/v2 v2.0.0-rc.27/go.mod h1:kL1v4iIjlalwm3gCYGvF4NLa3hs+aKEfRkNJvj4aoDU= github.com/beevik/ntp v1.4.3 h1:PlbTvE5NNy4QHmA4Mg57n7mcFTmr1W1j3gcK7L1lqho= github.com/beevik/ntp v1.4.3/go.mod h1:Unr8Zg+2dRn7d8bHFuehIMSvvUYssHMxW3Q5Nx4RW5Q= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= @@ -40,8 +42,9 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= @@ -337,10 +340,10 @@ golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= gonum.org/v1/gonum v0.15.0 h1:2lYxjRbTYyxkJxlhC+LvJIx3SsANPdRybu1tGj9/OrQ= gonum.org/v1/gonum v0.15.0/go.mod h1:xzZVBJBtS+Mz4q0Yl2LJTk+OxOg4jiXZ7qBoM0uISGo= -google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d h1:Aqf0fiIdUQEj0Gn9mKFFXoQfTTEaNopWpfVyYADxiSg= -google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:Od4k8V1LQSizPRUK4OzZ7TBE/20k+jPczUDAEyvn69Y= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d h1:k3zyW3BYYR30e8v3x0bTDdE9vpYFjZHK+HcyqkrppWk= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0= +google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= @@ -352,8 +355,8 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8= -gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= +gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= +gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s= gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= diff --git a/internal/engine/command/calculator/calculator.go b/internal/engine/command/calculator/calculator.go index 0eff9c39..382ea7c8 100644 --- a/internal/engine/command/calculator/calculator.go +++ b/internal/engine/command/calculator/calculator.go @@ -26,8 +26,7 @@ func NewCalculator(clientMgr *client.Mgr) Calculator { func (bc *Calculator) GetCommand() command.Command { subCmdCalcReward := command.Command{ Name: CalcRewardCommandName, - Desc: "Calculate how many PAC coins you will earn with your validator stake", - Help: "Provide a stake amount between 1 to 100, please avoid using float numbers like: 1.9 or PAC suffix", + Help: "Calculate how many PAC coins you will earn with your validator stake", Args: []command.Args{ { Name: "stake", @@ -47,8 +46,7 @@ func (bc *Calculator) GetCommand() command.Command { subCmdCalcFee := command.Command{ Name: CalcFeeCommandName, - Desc: "Calculate fee of a transaction with providing amount", - Help: "Provide your amount in PAC, please avoid using float numbers like: 1.9 or PAC suffix", + Help: "Calculate fee of a transaction with providing amount", Args: []command.Args{ { Name: "amount", @@ -63,8 +61,7 @@ func (bc *Calculator) GetCommand() command.Command { cmdBlockchain := command.Command{ Name: CommandName, - Desc: "Calculator information and tools", - Help: "", + Help: "Calculator information and tools", Args: nil, AppIDs: entity.AllAppIDs(), SubCommands: make([]command.Command, 0), diff --git a/internal/engine/command/command.go b/internal/engine/command/command.go index 721b016f..a3e1d601 100644 --- a/internal/engine/command/command.go +++ b/internal/engine/command/command.go @@ -27,7 +27,6 @@ type Command struct { Emoji string Color string Name string - Desc string Help string Args []Args //! should be nil for commands. AppIDs []entity.AppID @@ -49,7 +48,7 @@ type CommandResult struct { func (cmd *Command) SuccessfulResult(message string, a ...interface{}) CommandResult { return CommandResult{ Color: cmd.Color, - Title: fmt.Sprintf("%v %v", cmd.Desc, cmd.Emoji), + Title: fmt.Sprintf("%v %v", cmd.Help, cmd.Emoji), Message: fmt.Sprintf(message, a...), Successful: true, } @@ -58,7 +57,7 @@ func (cmd *Command) SuccessfulResult(message string, a ...interface{}) CommandRe func (cmd *Command) FailedResult(message string, a ...interface{}) CommandResult { return CommandResult{ Color: cmd.Color, - Title: fmt.Sprintf("%v %v", cmd.Desc, cmd.Emoji), + Title: fmt.Sprintf("%v %v", cmd.Help, cmd.Emoji), Message: fmt.Sprintf(message, a...), Successful: false, } @@ -71,7 +70,7 @@ func (cmd *Command) ErrorResult(err error) CommandResult { func (cmd *Command) HelpResult() CommandResult { return CommandResult{ Color: cmd.Color, - Title: fmt.Sprintf("%v %v", cmd.Desc, cmd.Emoji), + Title: fmt.Sprintf("%v %v", cmd.Help, cmd.Emoji), Message: cmd.HelpMessage(), Successful: false, } @@ -106,7 +105,7 @@ func (cmd *Command) HelpMessage() string { help := cmd.Help help += "\n\nAvailable commands:\n" for _, sc := range cmd.SubCommands { - help += fmt.Sprintf(" %-12s %s\n", sc.Name, sc.Desc) + help += fmt.Sprintf(" %-12s %s\n", sc.Name, sc.Help) } return help } @@ -122,7 +121,7 @@ func (cmd *Command) AddSubCommand(subCmd Command) { func (cmd *Command) AddHelpSubCommand() { helpCmd := Command{ Name: "help", - Desc: fmt.Sprintf("Help for %v command", cmd.Name), + Help: fmt.Sprintf("Help for %v command", cmd.Name), AppIDs: entity.AllAppIDs(), Handler: func(_ Command, _ entity.AppID, _ string, _ ...string) CommandResult { return cmd.SuccessfulResult(cmd.HelpMessage()) diff --git a/internal/engine/command/market/market.go b/internal/engine/command/market/market.go index 1772ed8b..daed992f 100644 --- a/internal/engine/command/market/market.go +++ b/internal/engine/command/market/market.go @@ -28,8 +28,7 @@ func NewMarket(clientMgr *client.Mgr, priceCache cache.Cache[string, entity.Pric func (m *Market) GetCommand() command.Command { subCmdPrice := command.Command{ Name: PriceCommandName, - Desc: "Shows the last price of PAC coin on the markets", - Help: "", + Help: "Shows the last price of PAC coin on the markets", Args: []command.Args{}, SubCommands: nil, AppIDs: entity.AllAppIDs(), @@ -38,8 +37,7 @@ func (m *Market) GetCommand() command.Command { cmdMarket := command.Command{ Name: CommandName, - Desc: "Blockchain data and information", - Help: "", + Help: "Blockchain data and information", Args: nil, AppIDs: entity.AllAppIDs(), SubCommands: make([]command.Command, 0), diff --git a/internal/engine/command/market/price_test.go b/internal/engine/command/market/price_test.go index c7ebdab7..d542f1ee 100644 --- a/internal/engine/command/market/price_test.go +++ b/internal/engine/command/market/price_test.go @@ -21,8 +21,7 @@ func setup() (Market, command.Command) { return m, command.Command{ Name: PriceCommandName, - Desc: "Shows the last price of PAC coin on the markets", - Help: "", + Help: "Shows the last price of PAC coin on the markets", Args: []command.Args{}, SubCommands: nil, AppIDs: entity.AllAppIDs(), diff --git a/internal/engine/command/network/network.go b/internal/engine/command/network/network.go index 70f7a882..b44acc76 100644 --- a/internal/engine/command/network/network.go +++ b/internal/engine/command/network/network.go @@ -62,8 +62,7 @@ type NetStatus struct { func (n *Network) GetCommand() command.Command { subCmdNodeInfo := command.Command{ Name: NodeInfoCommandName, - Desc: "View the information of a node", - Help: "Provide your validator address on the specific node to get the validator and node info", + Help: "View the information of a node", Args: []command.Args{ { Name: "validator_address", @@ -78,8 +77,7 @@ func (n *Network) GetCommand() command.Command { subCmdHealth := command.Command{ Name: HealthCommandName, - Desc: "Checking network health status", - Help: "", + Help: "Checking network health status", Args: []command.Args{}, SubCommands: nil, AppIDs: entity.AllAppIDs(), @@ -88,8 +86,7 @@ func (n *Network) GetCommand() command.Command { subCmdStatus := command.Command{ Name: StatusCommandName, - Desc: "Network statistics", - Help: "", + Help: "Network statistics", Args: []command.Args{}, SubCommands: nil, AppIDs: entity.AllAppIDs(), @@ -98,8 +95,7 @@ func (n *Network) GetCommand() command.Command { cmdNetwork := command.Command{ Name: CommandName, - Desc: "Network related commands", - Help: "", + Help: "Network related commands", Args: nil, AppIDs: entity.AllAppIDs(), SubCommands: make([]command.Command, 0), diff --git a/internal/engine/command/phoenix/faucet.go b/internal/engine/command/phoenix/faucet.go index 8bf6eee5..feadc225 100644 --- a/internal/engine/command/phoenix/faucet.go +++ b/internal/engine/command/phoenix/faucet.go @@ -21,7 +21,7 @@ func (pt *Phoenix) faucetHandler(cmd command.Command, _ entity.AppID, _ string, return cmd.FailedResult("Uh, you used your share of faucets today!") } - txID, err := pt.wallet.TransferTransaction(toAddr, "Phoenix Testnet Pagu PhoenixFaucet", int64(pt.faucetAmount)) //! define me on config? + txID, err := pt.wallet.TransferTransaction(toAddr, "Phoenix Testnet Pagu PhoenixFaucet", int64(pt.faucetAmount)) if err != nil { return cmd.ErrorResult(err) } diff --git a/internal/engine/command/phoenix/faucet_test.go b/internal/engine/command/phoenix/faucet_test.go index 211b3977..0f317f57 100644 --- a/internal/engine/command/phoenix/faucet_test.go +++ b/internal/engine/command/phoenix/faucet_test.go @@ -15,7 +15,6 @@ func setup() (Phoenix, command.Command) { return p, command.Command{ Name: FaucetCommandName, - Desc: "", Help: "", Args: []command.Args{}, SubCommands: nil, diff --git a/internal/engine/command/phoenix/phoenix.go b/internal/engine/command/phoenix/phoenix.go index f9ecbe2e..be07c097 100644 --- a/internal/engine/command/phoenix/phoenix.go +++ b/internal/engine/command/phoenix/phoenix.go @@ -41,8 +41,7 @@ func (pt *Phoenix) GetCommand() command.Command { subCmdStatus := command.Command{ Name: StatusCommandName, - Desc: "Phoenix test-network statistics", - Help: "", + Help: "Phoenix test-network statistics", Args: []command.Args{}, SubCommands: nil, AppIDs: entity.AllAppIDs(), @@ -52,8 +51,7 @@ func (pt *Phoenix) GetCommand() command.Command { subCmdFaucet := command.Command{ Name: FaucetCommandName, - Desc: fmt.Sprintf("Get %d tPAC Coins on Phoenix Testnet for Testing your code or project", pt.faucetAmount), - Help: "There is a limit that you can only get faucets 1 time per day with each user ID and address", + Help: fmt.Sprintf("Get %d tPAC Coins on Phoenix Testnet for Testing your code or project", pt.faucetAmount), Args: []command.Args{ { Name: "address", @@ -69,8 +67,7 @@ func (pt *Phoenix) GetCommand() command.Command { cmdPhoenix := command.Command{ Name: CommandName, - Desc: "Phoenix Testnet tools and utils for developers", - Help: "", + Help: "Phoenix Testnet tools and utils for developers", Args: nil, AppIDs: entity.AllAppIDs(), SubCommands: make([]command.Command, 0), diff --git a/internal/engine/command/voucher/claim.go b/internal/engine/command/voucher/claim.go index eabc74de..05bf0fe1 100644 --- a/internal/engine/command/voucher/claim.go +++ b/internal/engine/command/voucher/claim.go @@ -2,6 +2,7 @@ package voucher import ( "errors" + "time" "github.com/pagu-project/Pagu/internal/engine/command" "github.com/pagu-project/Pagu/internal/entity" @@ -18,6 +19,11 @@ func (v *Voucher) claimHandler(cmd command.Command, _ entity.AppID, _ string, ar return cmd.ErrorResult(errors.New("voucher code is not valid")) } + now := time.Now().Month() + if voucher.CreatedAt.Month() >= (now + time.Month(voucher.ValidMonths)) { + return cmd.ErrorResult(errors.New("voucher is expired")) + } + if len(voucher.TxHash) > 0 { return cmd.ErrorResult(errors.New("voucher code claimed before")) } diff --git a/internal/engine/command/voucher/create.go b/internal/engine/command/voucher/create.go new file mode 100644 index 00000000..9942f393 --- /dev/null +++ b/internal/engine/command/voucher/create.go @@ -0,0 +1,49 @@ +package voucher + +import ( + "strconv" + + "github.com/pagu-project/Pagu/internal/engine/command" + "github.com/pagu-project/Pagu/internal/entity" +) + +func (v *Voucher) createHandler(cmd command.Command, _ entity.AppID, callerID string, args ...string) command.CommandResult { + //! Admin only check + + cID, err := strconv.Atoi(callerID) + if err != nil { + return cmd.ErrorResult(err) + } + + recipient := args[0] + description := args[1] + validMonths := args[2] + amount := args[3] + discordID := args[4] + code := args[5] + + expireMonths, err := strconv.Atoi(validMonths) + if err != nil { + return cmd.ErrorResult(err) + } + + intAmount, err := strconv.Atoi(amount) + if err != nil { + return cmd.ErrorResult(err) + } + + err = v.db.AddVoucher(&entity.Voucher{ + Creator: uint(cID), + Code: code, + Desc: description, + DiscordID: discordID, + Recipient: recipient, + ValidMonths: uint(expireMonths), + Amount: uint(intAmount), + }) + if err != nil { + return cmd.ErrorResult(err) + } + + return cmd.SuccessfulResult("Voucher crated successfully!") +} diff --git a/internal/engine/command/voucher/voucher.go b/internal/engine/command/voucher/voucher.go index 800de4d0..4eb5e1a9 100644 --- a/internal/engine/command/voucher/voucher.go +++ b/internal/engine/command/voucher/voucher.go @@ -9,9 +9,10 @@ import ( ) const ( - CommandName = "voucher" - ClaimCommandName = "claim" - HelpCommandName = "help" + CommandName = "voucher" + ClaimCommandName = "claim" + CreateCommandName = "create" + HelpCommandName = "help" ) type Voucher struct { @@ -31,8 +32,7 @@ func NewVoucher(db *repository.DB, wallet *wallet.Wallet, cli *client.Mgr) Vouch func (v *Voucher) GetCommand() command.Command { subCmdClaim := command.Command{ Name: ClaimCommandName, - Desc: "Claim your voucher coins and bond to validator", - Help: "", + Help: "Claim your voucher coins and bond to validator", Args: []command.Args{ { Name: "code", @@ -50,10 +50,49 @@ func (v *Voucher) GetCommand() command.Command { Handler: v.claimHandler, } + subCmdCreate := command.Command{ + Name: ClaimCommandName, + Help: "Add a new voucher to database", + Args: []command.Args{ + { + Name: "recipient", + Desc: "Indicates the name of the recipient of the voucher", + Optional: false, + }, + { + Name: "description", + Desc: "Describes the reason for issuing the voucher", + Optional: false, + }, + { + Name: "valid-months", + Desc: "Indicates how many months the voucher is valid after it is issued", + Optional: false, + }, + { + Name: "amount", + Desc: "Amount of PAC to bond", + Optional: false, + }, + { + Name: "discord-id", + Desc: "Recipient Discord ID", + Optional: false, + }, + { + Name: "code", + Desc: "The voucher code", + Optional: false, + }, + }, + SubCommands: nil, + AppIDs: entity.AllAppIDs(), + Handler: v.createHandler, + } + cmdVoucher := command.Command{ Name: CommandName, - Desc: "Voucher Commands", - Help: "", + Help: "Voucher Commands", Args: nil, AppIDs: entity.AllAppIDs(), SubCommands: make([]command.Command, 0), @@ -62,5 +101,6 @@ func (v *Voucher) GetCommand() command.Command { } cmdVoucher.AddSubCommand(subCmdClaim) + cmdVoucher.AddSubCommand(subCmdCreate) return cmdVoucher } diff --git a/internal/engine/command/zealy/zealy.go b/internal/engine/command/zealy/zealy.go index 8b91df49..bddd904d 100644 --- a/internal/engine/command/zealy/zealy.go +++ b/internal/engine/command/zealy/zealy.go @@ -29,8 +29,7 @@ func NewZealy(db *repository.DB, wallet *wallet.Wallet) Zealy { func (z *Zealy) GetCommand() command.Command { subCmdClaim := command.Command{ Name: ClaimCommandName, - Desc: "Claim your Zealy Reward", - Help: "", + Help: "Claim your Zealy Reward", Args: []command.Args{ { Name: "address", @@ -45,8 +44,7 @@ func (z *Zealy) GetCommand() command.Command { subCmdStatus := command.Command{ Name: StatusCommandName, - Desc: "Status of Zealy reward claims", - Help: "", + Help: "Status of Zealy reward claims", Args: nil, SubCommands: nil, AppIDs: entity.AllAppIDs(), @@ -55,8 +53,7 @@ func (z *Zealy) GetCommand() command.Command { cmdZealy := command.Command{ Name: CommandName, - Desc: "Zealy Commands", - Help: "", + Help: "Zealy Commands", Args: nil, AppIDs: entity.AllAppIDs(), SubCommands: make([]command.Command, 0), diff --git a/internal/engine/engine.go b/internal/engine/engine.go index ece003a8..90dcde2b 100644 --- a/internal/engine/engine.go +++ b/internal/engine/engine.go @@ -98,8 +98,7 @@ func newBotEngine(ctx context.Context, cnl context.CancelFunc, db *repository.DB rootCmd := command.Command{ Emoji: "🤖", Name: "pagu", - Desc: "Root Command", - Help: "Pagu Help Command", + Help: "Root Command", AppIDs: entity.AllAppIDs(), SubCommands: make([]command.Command, 3), } diff --git a/internal/entity/voucher.go b/internal/entity/voucher.go index 3fb73440..15f8f8da 100644 --- a/internal/entity/voucher.go +++ b/internal/entity/voucher.go @@ -5,12 +5,15 @@ import ( ) type Voucher struct { - ID uint `gorm:"primaryKey;unique"` - Creator uint `gorm:"size:255"` - Code string `gorm:"size:8"` - Amount uint - TxHash string - + ID uint `gorm:"primaryKey;unique"` + Creator uint `gorm:"size:255"` + Code string `gorm:"size:8"` + Desc string + DiscordID string + Recipient string + ValidMonths uint + Amount uint + TxHash string gorm.Model } diff --git a/internal/platforms/discord/discord.go b/internal/platforms/discord/discord.go index 6cc3c4dc..d5d7fbfa 100644 --- a/internal/platforms/discord/discord.go +++ b/internal/platforms/discord/discord.go @@ -93,27 +93,27 @@ func (bot *DiscordBot) registerCommands() error { } } - log.Info("registering new command", "name", beCmd.Name, "desc", beCmd.Desc, "index", i, "object", beCmd) + log.Info("registering new command", "name", beCmd.Name, "desc", beCmd.Help, "index", i, "object", beCmd) discordCmd := discordgo.ApplicationCommand{ Type: discordgo.ChatApplicationCommand, Name: beCmd.Name, - Description: beCmd.Desc, + Description: beCmd.Help, } if beCmd.HasSubCommand() { for _, sCmd := range beCmd.SubCommands { - if sCmd.Name == "" || sCmd.Desc == "" { + if sCmd.Name == "" || sCmd.Help == "" { continue } log.Info("adding command sub-command", "command", beCmd.Name, - "sub-command", sCmd.Name, "desc", sCmd.Desc) + "sub-command", sCmd.Name, "desc", sCmd.Help) subCmd := &discordgo.ApplicationCommandOption{ Type: discordgo.ApplicationCommandOptionSubCommand, Name: sCmd.Name, - Description: sCmd.Desc, + Description: sCmd.Help, } for _, arg := range sCmd.Args { diff --git a/internal/repository/voucher.go b/internal/repository/voucher.go index fc272153..7a54f8fe 100644 --- a/internal/repository/voucher.go +++ b/internal/repository/voucher.go @@ -4,6 +4,17 @@ import ( "github.com/pagu-project/Pagu/internal/entity" ) +func (db *DB) AddVoucher(v *entity.Voucher) error { + tx := db.Create(v) + if tx.Error != nil { + return WriteError{ + Message: tx.Error.Error(), + } + } + + return nil +} + func (db *DB) GetVoucherByCode(code string) (entity.Voucher, error) { var voucher entity.Voucher err := db.Model(&entity.Voucher{}).Where("code = ?", code).First(&voucher).Error diff --git a/vendor/filippo.io/edwards25519/LICENSE b/vendor/filippo.io/edwards25519/LICENSE new file mode 100644 index 00000000..6a66aea5 --- /dev/null +++ b/vendor/filippo.io/edwards25519/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/filippo.io/edwards25519/README.md b/vendor/filippo.io/edwards25519/README.md new file mode 100644 index 00000000..24e2457d --- /dev/null +++ b/vendor/filippo.io/edwards25519/README.md @@ -0,0 +1,14 @@ +# filippo.io/edwards25519 + +``` +import "filippo.io/edwards25519" +``` + +This library implements the edwards25519 elliptic curve, exposing the necessary APIs to build a wide array of higher-level primitives. +Read the docs at [pkg.go.dev/filippo.io/edwards25519](https://pkg.go.dev/filippo.io/edwards25519). + +The code is originally derived from Adam Langley's internal implementation in the Go standard library, and includes George Tankersley's [performance improvements](https://golang.org/cl/71950). It was then further developed by Henry de Valence for use in ristretto255, and was finally [merged back into the Go standard library](https://golang.org/cl/276272) as of Go 1.17. It now tracks the upstream codebase and extends it with additional functionality. + +Most users don't need this package, and should instead use `crypto/ed25519` for signatures, `golang.org/x/crypto/curve25519` for Diffie-Hellman, or `github.com/gtank/ristretto255` for prime order group logic. However, for anyone currently using a fork of `crypto/internal/edwards25519`/`crypto/ed25519/internal/edwards25519` or `github.com/agl/edwards25519`, this package should be a safer, faster, and more powerful alternative. + +Since this package is meant to curb proliferation of edwards25519 implementations in the Go ecosystem, it welcomes requests for new APIs or reviewable performance improvements. diff --git a/vendor/filippo.io/edwards25519/doc.go b/vendor/filippo.io/edwards25519/doc.go new file mode 100644 index 00000000..ab6aaebc --- /dev/null +++ b/vendor/filippo.io/edwards25519/doc.go @@ -0,0 +1,20 @@ +// Copyright (c) 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package edwards25519 implements group logic for the twisted Edwards curve +// +// -x^2 + y^2 = 1 + -(121665/121666)*x^2*y^2 +// +// This is better known as the Edwards curve equivalent to Curve25519, and is +// the curve used by the Ed25519 signature scheme. +// +// Most users don't need this package, and should instead use crypto/ed25519 for +// signatures, golang.org/x/crypto/curve25519 for Diffie-Hellman, or +// github.com/gtank/ristretto255 for prime order group logic. +// +// However, developers who do need to interact with low-level edwards25519 +// operations can use this package, which is an extended version of +// crypto/internal/edwards25519 from the standard library repackaged as +// an importable module. +package edwards25519 diff --git a/vendor/filippo.io/edwards25519/edwards25519.go b/vendor/filippo.io/edwards25519/edwards25519.go new file mode 100644 index 00000000..a744da2c --- /dev/null +++ b/vendor/filippo.io/edwards25519/edwards25519.go @@ -0,0 +1,427 @@ +// Copyright (c) 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package edwards25519 + +import ( + "errors" + + "filippo.io/edwards25519/field" +) + +// Point types. + +type projP1xP1 struct { + X, Y, Z, T field.Element +} + +type projP2 struct { + X, Y, Z field.Element +} + +// Point represents a point on the edwards25519 curve. +// +// This type works similarly to math/big.Int, and all arguments and receivers +// are allowed to alias. +// +// The zero value is NOT valid, and it may be used only as a receiver. +type Point struct { + // Make the type not comparable (i.e. used with == or as a map key), as + // equivalent points can be represented by different Go values. + _ incomparable + + // The point is internally represented in extended coordinates (X, Y, Z, T) + // where x = X/Z, y = Y/Z, and xy = T/Z per https://eprint.iacr.org/2008/522. + x, y, z, t field.Element +} + +type incomparable [0]func() + +func checkInitialized(points ...*Point) { + for _, p := range points { + if p.x == (field.Element{}) && p.y == (field.Element{}) { + panic("edwards25519: use of uninitialized Point") + } + } +} + +type projCached struct { + YplusX, YminusX, Z, T2d field.Element +} + +type affineCached struct { + YplusX, YminusX, T2d field.Element +} + +// Constructors. + +func (v *projP2) Zero() *projP2 { + v.X.Zero() + v.Y.One() + v.Z.One() + return v +} + +// identity is the point at infinity. +var identity, _ = new(Point).SetBytes([]byte{ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) + +// NewIdentityPoint returns a new Point set to the identity. +func NewIdentityPoint() *Point { + return new(Point).Set(identity) +} + +// generator is the canonical curve basepoint. See TestGenerator for the +// correspondence of this encoding with the values in RFC 8032. +var generator, _ = new(Point).SetBytes([]byte{ + 0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}) + +// NewGeneratorPoint returns a new Point set to the canonical generator. +func NewGeneratorPoint() *Point { + return new(Point).Set(generator) +} + +func (v *projCached) Zero() *projCached { + v.YplusX.One() + v.YminusX.One() + v.Z.One() + v.T2d.Zero() + return v +} + +func (v *affineCached) Zero() *affineCached { + v.YplusX.One() + v.YminusX.One() + v.T2d.Zero() + return v +} + +// Assignments. + +// Set sets v = u, and returns v. +func (v *Point) Set(u *Point) *Point { + *v = *u + return v +} + +// Encoding. + +// Bytes returns the canonical 32-byte encoding of v, according to RFC 8032, +// Section 5.1.2. +func (v *Point) Bytes() []byte { + // This function is outlined to make the allocations inline in the caller + // rather than happen on the heap. + var buf [32]byte + return v.bytes(&buf) +} + +func (v *Point) bytes(buf *[32]byte) []byte { + checkInitialized(v) + + var zInv, x, y field.Element + zInv.Invert(&v.z) // zInv = 1 / Z + x.Multiply(&v.x, &zInv) // x = X / Z + y.Multiply(&v.y, &zInv) // y = Y / Z + + out := copyFieldElement(buf, &y) + out[31] |= byte(x.IsNegative() << 7) + return out +} + +var feOne = new(field.Element).One() + +// SetBytes sets v = x, where x is a 32-byte encoding of v. If x does not +// represent a valid point on the curve, SetBytes returns nil and an error and +// the receiver is unchanged. Otherwise, SetBytes returns v. +// +// Note that SetBytes accepts all non-canonical encodings of valid points. +// That is, it follows decoding rules that match most implementations in +// the ecosystem rather than RFC 8032. +func (v *Point) SetBytes(x []byte) (*Point, error) { + // Specifically, the non-canonical encodings that are accepted are + // 1) the ones where the field element is not reduced (see the + // (*field.Element).SetBytes docs) and + // 2) the ones where the x-coordinate is zero and the sign bit is set. + // + // Read more at https://hdevalence.ca/blog/2020-10-04-its-25519am, + // specifically the "Canonical A, R" section. + + y, err := new(field.Element).SetBytes(x) + if err != nil { + return nil, errors.New("edwards25519: invalid point encoding length") + } + + // -x² + y² = 1 + dx²y² + // x² + dx²y² = x²(dy² + 1) = y² - 1 + // x² = (y² - 1) / (dy² + 1) + + // u = y² - 1 + y2 := new(field.Element).Square(y) + u := new(field.Element).Subtract(y2, feOne) + + // v = dy² + 1 + vv := new(field.Element).Multiply(y2, d) + vv = vv.Add(vv, feOne) + + // x = +√(u/v) + xx, wasSquare := new(field.Element).SqrtRatio(u, vv) + if wasSquare == 0 { + return nil, errors.New("edwards25519: invalid point encoding") + } + + // Select the negative square root if the sign bit is set. + xxNeg := new(field.Element).Negate(xx) + xx = xx.Select(xxNeg, xx, int(x[31]>>7)) + + v.x.Set(xx) + v.y.Set(y) + v.z.One() + v.t.Multiply(xx, y) // xy = T / Z + + return v, nil +} + +func copyFieldElement(buf *[32]byte, v *field.Element) []byte { + copy(buf[:], v.Bytes()) + return buf[:] +} + +// Conversions. + +func (v *projP2) FromP1xP1(p *projP1xP1) *projP2 { + v.X.Multiply(&p.X, &p.T) + v.Y.Multiply(&p.Y, &p.Z) + v.Z.Multiply(&p.Z, &p.T) + return v +} + +func (v *projP2) FromP3(p *Point) *projP2 { + v.X.Set(&p.x) + v.Y.Set(&p.y) + v.Z.Set(&p.z) + return v +} + +func (v *Point) fromP1xP1(p *projP1xP1) *Point { + v.x.Multiply(&p.X, &p.T) + v.y.Multiply(&p.Y, &p.Z) + v.z.Multiply(&p.Z, &p.T) + v.t.Multiply(&p.X, &p.Y) + return v +} + +func (v *Point) fromP2(p *projP2) *Point { + v.x.Multiply(&p.X, &p.Z) + v.y.Multiply(&p.Y, &p.Z) + v.z.Square(&p.Z) + v.t.Multiply(&p.X, &p.Y) + return v +} + +// d is a constant in the curve equation. +var d, _ = new(field.Element).SetBytes([]byte{ + 0xa3, 0x78, 0x59, 0x13, 0xca, 0x4d, 0xeb, 0x75, + 0xab, 0xd8, 0x41, 0x41, 0x4d, 0x0a, 0x70, 0x00, + 0x98, 0xe8, 0x79, 0x77, 0x79, 0x40, 0xc7, 0x8c, + 0x73, 0xfe, 0x6f, 0x2b, 0xee, 0x6c, 0x03, 0x52}) +var d2 = new(field.Element).Add(d, d) + +func (v *projCached) FromP3(p *Point) *projCached { + v.YplusX.Add(&p.y, &p.x) + v.YminusX.Subtract(&p.y, &p.x) + v.Z.Set(&p.z) + v.T2d.Multiply(&p.t, d2) + return v +} + +func (v *affineCached) FromP3(p *Point) *affineCached { + v.YplusX.Add(&p.y, &p.x) + v.YminusX.Subtract(&p.y, &p.x) + v.T2d.Multiply(&p.t, d2) + + var invZ field.Element + invZ.Invert(&p.z) + v.YplusX.Multiply(&v.YplusX, &invZ) + v.YminusX.Multiply(&v.YminusX, &invZ) + v.T2d.Multiply(&v.T2d, &invZ) + return v +} + +// (Re)addition and subtraction. + +// Add sets v = p + q, and returns v. +func (v *Point) Add(p, q *Point) *Point { + checkInitialized(p, q) + qCached := new(projCached).FromP3(q) + result := new(projP1xP1).Add(p, qCached) + return v.fromP1xP1(result) +} + +// Subtract sets v = p - q, and returns v. +func (v *Point) Subtract(p, q *Point) *Point { + checkInitialized(p, q) + qCached := new(projCached).FromP3(q) + result := new(projP1xP1).Sub(p, qCached) + return v.fromP1xP1(result) +} + +func (v *projP1xP1) Add(p *Point, q *projCached) *projP1xP1 { + var YplusX, YminusX, PP, MM, TT2d, ZZ2 field.Element + + YplusX.Add(&p.y, &p.x) + YminusX.Subtract(&p.y, &p.x) + + PP.Multiply(&YplusX, &q.YplusX) + MM.Multiply(&YminusX, &q.YminusX) + TT2d.Multiply(&p.t, &q.T2d) + ZZ2.Multiply(&p.z, &q.Z) + + ZZ2.Add(&ZZ2, &ZZ2) + + v.X.Subtract(&PP, &MM) + v.Y.Add(&PP, &MM) + v.Z.Add(&ZZ2, &TT2d) + v.T.Subtract(&ZZ2, &TT2d) + return v +} + +func (v *projP1xP1) Sub(p *Point, q *projCached) *projP1xP1 { + var YplusX, YminusX, PP, MM, TT2d, ZZ2 field.Element + + YplusX.Add(&p.y, &p.x) + YminusX.Subtract(&p.y, &p.x) + + PP.Multiply(&YplusX, &q.YminusX) // flipped sign + MM.Multiply(&YminusX, &q.YplusX) // flipped sign + TT2d.Multiply(&p.t, &q.T2d) + ZZ2.Multiply(&p.z, &q.Z) + + ZZ2.Add(&ZZ2, &ZZ2) + + v.X.Subtract(&PP, &MM) + v.Y.Add(&PP, &MM) + v.Z.Subtract(&ZZ2, &TT2d) // flipped sign + v.T.Add(&ZZ2, &TT2d) // flipped sign + return v +} + +func (v *projP1xP1) AddAffine(p *Point, q *affineCached) *projP1xP1 { + var YplusX, YminusX, PP, MM, TT2d, Z2 field.Element + + YplusX.Add(&p.y, &p.x) + YminusX.Subtract(&p.y, &p.x) + + PP.Multiply(&YplusX, &q.YplusX) + MM.Multiply(&YminusX, &q.YminusX) + TT2d.Multiply(&p.t, &q.T2d) + + Z2.Add(&p.z, &p.z) + + v.X.Subtract(&PP, &MM) + v.Y.Add(&PP, &MM) + v.Z.Add(&Z2, &TT2d) + v.T.Subtract(&Z2, &TT2d) + return v +} + +func (v *projP1xP1) SubAffine(p *Point, q *affineCached) *projP1xP1 { + var YplusX, YminusX, PP, MM, TT2d, Z2 field.Element + + YplusX.Add(&p.y, &p.x) + YminusX.Subtract(&p.y, &p.x) + + PP.Multiply(&YplusX, &q.YminusX) // flipped sign + MM.Multiply(&YminusX, &q.YplusX) // flipped sign + TT2d.Multiply(&p.t, &q.T2d) + + Z2.Add(&p.z, &p.z) + + v.X.Subtract(&PP, &MM) + v.Y.Add(&PP, &MM) + v.Z.Subtract(&Z2, &TT2d) // flipped sign + v.T.Add(&Z2, &TT2d) // flipped sign + return v +} + +// Doubling. + +func (v *projP1xP1) Double(p *projP2) *projP1xP1 { + var XX, YY, ZZ2, XplusYsq field.Element + + XX.Square(&p.X) + YY.Square(&p.Y) + ZZ2.Square(&p.Z) + ZZ2.Add(&ZZ2, &ZZ2) + XplusYsq.Add(&p.X, &p.Y) + XplusYsq.Square(&XplusYsq) + + v.Y.Add(&YY, &XX) + v.Z.Subtract(&YY, &XX) + + v.X.Subtract(&XplusYsq, &v.Y) + v.T.Subtract(&ZZ2, &v.Z) + return v +} + +// Negation. + +// Negate sets v = -p, and returns v. +func (v *Point) Negate(p *Point) *Point { + checkInitialized(p) + v.x.Negate(&p.x) + v.y.Set(&p.y) + v.z.Set(&p.z) + v.t.Negate(&p.t) + return v +} + +// Equal returns 1 if v is equivalent to u, and 0 otherwise. +func (v *Point) Equal(u *Point) int { + checkInitialized(v, u) + + var t1, t2, t3, t4 field.Element + t1.Multiply(&v.x, &u.z) + t2.Multiply(&u.x, &v.z) + t3.Multiply(&v.y, &u.z) + t4.Multiply(&u.y, &v.z) + + return t1.Equal(&t2) & t3.Equal(&t4) +} + +// Constant-time operations + +// Select sets v to a if cond == 1 and to b if cond == 0. +func (v *projCached) Select(a, b *projCached, cond int) *projCached { + v.YplusX.Select(&a.YplusX, &b.YplusX, cond) + v.YminusX.Select(&a.YminusX, &b.YminusX, cond) + v.Z.Select(&a.Z, &b.Z, cond) + v.T2d.Select(&a.T2d, &b.T2d, cond) + return v +} + +// Select sets v to a if cond == 1 and to b if cond == 0. +func (v *affineCached) Select(a, b *affineCached, cond int) *affineCached { + v.YplusX.Select(&a.YplusX, &b.YplusX, cond) + v.YminusX.Select(&a.YminusX, &b.YminusX, cond) + v.T2d.Select(&a.T2d, &b.T2d, cond) + return v +} + +// CondNeg negates v if cond == 1 and leaves it unchanged if cond == 0. +func (v *projCached) CondNeg(cond int) *projCached { + v.YplusX.Swap(&v.YminusX, cond) + v.T2d.Select(new(field.Element).Negate(&v.T2d), &v.T2d, cond) + return v +} + +// CondNeg negates v if cond == 1 and leaves it unchanged if cond == 0. +func (v *affineCached) CondNeg(cond int) *affineCached { + v.YplusX.Swap(&v.YminusX, cond) + v.T2d.Select(new(field.Element).Negate(&v.T2d), &v.T2d, cond) + return v +} diff --git a/vendor/filippo.io/edwards25519/extra.go b/vendor/filippo.io/edwards25519/extra.go new file mode 100644 index 00000000..d152d68f --- /dev/null +++ b/vendor/filippo.io/edwards25519/extra.go @@ -0,0 +1,349 @@ +// Copyright (c) 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package edwards25519 + +// This file contains additional functionality that is not included in the +// upstream crypto/internal/edwards25519 package. + +import ( + "errors" + + "filippo.io/edwards25519/field" +) + +// ExtendedCoordinates returns v in extended coordinates (X:Y:Z:T) where +// x = X/Z, y = Y/Z, and xy = T/Z as in https://eprint.iacr.org/2008/522. +func (v *Point) ExtendedCoordinates() (X, Y, Z, T *field.Element) { + // This function is outlined to make the allocations inline in the caller + // rather than happen on the heap. Don't change the style without making + // sure it doesn't increase the inliner cost. + var e [4]field.Element + X, Y, Z, T = v.extendedCoordinates(&e) + return +} + +func (v *Point) extendedCoordinates(e *[4]field.Element) (X, Y, Z, T *field.Element) { + checkInitialized(v) + X = e[0].Set(&v.x) + Y = e[1].Set(&v.y) + Z = e[2].Set(&v.z) + T = e[3].Set(&v.t) + return +} + +// SetExtendedCoordinates sets v = (X:Y:Z:T) in extended coordinates where +// x = X/Z, y = Y/Z, and xy = T/Z as in https://eprint.iacr.org/2008/522. +// +// If the coordinates are invalid or don't represent a valid point on the curve, +// SetExtendedCoordinates returns nil and an error and the receiver is +// unchanged. Otherwise, SetExtendedCoordinates returns v. +func (v *Point) SetExtendedCoordinates(X, Y, Z, T *field.Element) (*Point, error) { + if !isOnCurve(X, Y, Z, T) { + return nil, errors.New("edwards25519: invalid point coordinates") + } + v.x.Set(X) + v.y.Set(Y) + v.z.Set(Z) + v.t.Set(T) + return v, nil +} + +func isOnCurve(X, Y, Z, T *field.Element) bool { + var lhs, rhs field.Element + XX := new(field.Element).Square(X) + YY := new(field.Element).Square(Y) + ZZ := new(field.Element).Square(Z) + TT := new(field.Element).Square(T) + // -x² + y² = 1 + dx²y² + // -(X/Z)² + (Y/Z)² = 1 + d(T/Z)² + // -X² + Y² = Z² + dT² + lhs.Subtract(YY, XX) + rhs.Multiply(d, TT).Add(&rhs, ZZ) + if lhs.Equal(&rhs) != 1 { + return false + } + // xy = T/Z + // XY/Z² = T/Z + // XY = TZ + lhs.Multiply(X, Y) + rhs.Multiply(T, Z) + return lhs.Equal(&rhs) == 1 +} + +// BytesMontgomery converts v to a point on the birationally-equivalent +// Curve25519 Montgomery curve, and returns its canonical 32 bytes encoding +// according to RFC 7748. +// +// Note that BytesMontgomery only encodes the u-coordinate, so v and -v encode +// to the same value. If v is the identity point, BytesMontgomery returns 32 +// zero bytes, analogously to the X25519 function. +// +// The lack of an inverse operation (such as SetMontgomeryBytes) is deliberate: +// while every valid edwards25519 point has a unique u-coordinate Montgomery +// encoding, X25519 accepts inputs on the quadratic twist, which don't correspond +// to any edwards25519 point, and every other X25519 input corresponds to two +// edwards25519 points. +func (v *Point) BytesMontgomery() []byte { + // This function is outlined to make the allocations inline in the caller + // rather than happen on the heap. + var buf [32]byte + return v.bytesMontgomery(&buf) +} + +func (v *Point) bytesMontgomery(buf *[32]byte) []byte { + checkInitialized(v) + + // RFC 7748, Section 4.1 provides the bilinear map to calculate the + // Montgomery u-coordinate + // + // u = (1 + y) / (1 - y) + // + // where y = Y / Z. + + var y, recip, u field.Element + + y.Multiply(&v.y, y.Invert(&v.z)) // y = Y / Z + recip.Invert(recip.Subtract(feOne, &y)) // r = 1/(1 - y) + u.Multiply(u.Add(feOne, &y), &recip) // u = (1 + y)*r + + return copyFieldElement(buf, &u) +} + +// MultByCofactor sets v = 8 * p, and returns v. +func (v *Point) MultByCofactor(p *Point) *Point { + checkInitialized(p) + result := projP1xP1{} + pp := (&projP2{}).FromP3(p) + result.Double(pp) + pp.FromP1xP1(&result) + result.Double(pp) + pp.FromP1xP1(&result) + result.Double(pp) + return v.fromP1xP1(&result) +} + +// Given k > 0, set s = s**(2*i). +func (s *Scalar) pow2k(k int) { + for i := 0; i < k; i++ { + s.Multiply(s, s) + } +} + +// Invert sets s to the inverse of a nonzero scalar v, and returns s. +// +// If t is zero, Invert returns zero. +func (s *Scalar) Invert(t *Scalar) *Scalar { + // Uses a hardcoded sliding window of width 4. + var table [8]Scalar + var tt Scalar + tt.Multiply(t, t) + table[0] = *t + for i := 0; i < 7; i++ { + table[i+1].Multiply(&table[i], &tt) + } + // Now table = [t**1, t**3, t**5, t**7, t**9, t**11, t**13, t**15] + // so t**k = t[k/2] for odd k + + // To compute the sliding window digits, use the following Sage script: + + // sage: import itertools + // sage: def sliding_window(w,k): + // ....: digits = [] + // ....: while k > 0: + // ....: if k % 2 == 1: + // ....: kmod = k % (2**w) + // ....: digits.append(kmod) + // ....: k = k - kmod + // ....: else: + // ....: digits.append(0) + // ....: k = k // 2 + // ....: return digits + + // Now we can compute s roughly as follows: + + // sage: s = 1 + // sage: for coeff in reversed(sliding_window(4,l-2)): + // ....: s = s*s + // ....: if coeff > 0 : + // ....: s = s*t**coeff + + // This works on one bit at a time, with many runs of zeros. + // The digits can be collapsed into [(count, coeff)] as follows: + + // sage: [(len(list(group)),d) for d,group in itertools.groupby(sliding_window(4,l-2))] + + // Entries of the form (k, 0) turn into pow2k(k) + // Entries of the form (1, coeff) turn into a squaring and then a table lookup. + // We can fold the squaring into the previous pow2k(k) as pow2k(k+1). + + *s = table[1/2] + s.pow2k(127 + 1) + s.Multiply(s, &table[1/2]) + s.pow2k(4 + 1) + s.Multiply(s, &table[9/2]) + s.pow2k(3 + 1) + s.Multiply(s, &table[11/2]) + s.pow2k(3 + 1) + s.Multiply(s, &table[13/2]) + s.pow2k(3 + 1) + s.Multiply(s, &table[15/2]) + s.pow2k(4 + 1) + s.Multiply(s, &table[7/2]) + s.pow2k(4 + 1) + s.Multiply(s, &table[15/2]) + s.pow2k(3 + 1) + s.Multiply(s, &table[5/2]) + s.pow2k(3 + 1) + s.Multiply(s, &table[1/2]) + s.pow2k(4 + 1) + s.Multiply(s, &table[15/2]) + s.pow2k(4 + 1) + s.Multiply(s, &table[15/2]) + s.pow2k(4 + 1) + s.Multiply(s, &table[7/2]) + s.pow2k(3 + 1) + s.Multiply(s, &table[3/2]) + s.pow2k(4 + 1) + s.Multiply(s, &table[11/2]) + s.pow2k(5 + 1) + s.Multiply(s, &table[11/2]) + s.pow2k(9 + 1) + s.Multiply(s, &table[9/2]) + s.pow2k(3 + 1) + s.Multiply(s, &table[3/2]) + s.pow2k(4 + 1) + s.Multiply(s, &table[3/2]) + s.pow2k(4 + 1) + s.Multiply(s, &table[3/2]) + s.pow2k(4 + 1) + s.Multiply(s, &table[9/2]) + s.pow2k(3 + 1) + s.Multiply(s, &table[7/2]) + s.pow2k(3 + 1) + s.Multiply(s, &table[3/2]) + s.pow2k(3 + 1) + s.Multiply(s, &table[13/2]) + s.pow2k(3 + 1) + s.Multiply(s, &table[7/2]) + s.pow2k(4 + 1) + s.Multiply(s, &table[9/2]) + s.pow2k(3 + 1) + s.Multiply(s, &table[15/2]) + s.pow2k(4 + 1) + s.Multiply(s, &table[11/2]) + + return s +} + +// MultiScalarMult sets v = sum(scalars[i] * points[i]), and returns v. +// +// Execution time depends only on the lengths of the two slices, which must match. +func (v *Point) MultiScalarMult(scalars []*Scalar, points []*Point) *Point { + if len(scalars) != len(points) { + panic("edwards25519: called MultiScalarMult with different size inputs") + } + checkInitialized(points...) + + // Proceed as in the single-base case, but share doublings + // between each point in the multiscalar equation. + + // Build lookup tables for each point + tables := make([]projLookupTable, len(points)) + for i := range tables { + tables[i].FromP3(points[i]) + } + // Compute signed radix-16 digits for each scalar + digits := make([][64]int8, len(scalars)) + for i := range digits { + digits[i] = scalars[i].signedRadix16() + } + + // Unwrap first loop iteration to save computing 16*identity + multiple := &projCached{} + tmp1 := &projP1xP1{} + tmp2 := &projP2{} + // Lookup-and-add the appropriate multiple of each input point + for j := range tables { + tables[j].SelectInto(multiple, digits[j][63]) + tmp1.Add(v, multiple) // tmp1 = v + x_(j,63)*Q in P1xP1 coords + v.fromP1xP1(tmp1) // update v + } + tmp2.FromP3(v) // set up tmp2 = v in P2 coords for next iteration + for i := 62; i >= 0; i-- { + tmp1.Double(tmp2) // tmp1 = 2*(prev) in P1xP1 coords + tmp2.FromP1xP1(tmp1) // tmp2 = 2*(prev) in P2 coords + tmp1.Double(tmp2) // tmp1 = 4*(prev) in P1xP1 coords + tmp2.FromP1xP1(tmp1) // tmp2 = 4*(prev) in P2 coords + tmp1.Double(tmp2) // tmp1 = 8*(prev) in P1xP1 coords + tmp2.FromP1xP1(tmp1) // tmp2 = 8*(prev) in P2 coords + tmp1.Double(tmp2) // tmp1 = 16*(prev) in P1xP1 coords + v.fromP1xP1(tmp1) // v = 16*(prev) in P3 coords + // Lookup-and-add the appropriate multiple of each input point + for j := range tables { + tables[j].SelectInto(multiple, digits[j][i]) + tmp1.Add(v, multiple) // tmp1 = v + x_(j,i)*Q in P1xP1 coords + v.fromP1xP1(tmp1) // update v + } + tmp2.FromP3(v) // set up tmp2 = v in P2 coords for next iteration + } + return v +} + +// VarTimeMultiScalarMult sets v = sum(scalars[i] * points[i]), and returns v. +// +// Execution time depends on the inputs. +func (v *Point) VarTimeMultiScalarMult(scalars []*Scalar, points []*Point) *Point { + if len(scalars) != len(points) { + panic("edwards25519: called VarTimeMultiScalarMult with different size inputs") + } + checkInitialized(points...) + + // Generalize double-base NAF computation to arbitrary sizes. + // Here all the points are dynamic, so we only use the smaller + // tables. + + // Build lookup tables for each point + tables := make([]nafLookupTable5, len(points)) + for i := range tables { + tables[i].FromP3(points[i]) + } + // Compute a NAF for each scalar + nafs := make([][256]int8, len(scalars)) + for i := range nafs { + nafs[i] = scalars[i].nonAdjacentForm(5) + } + + multiple := &projCached{} + tmp1 := &projP1xP1{} + tmp2 := &projP2{} + tmp2.Zero() + + // Move from high to low bits, doubling the accumulator + // at each iteration and checking whether there is a nonzero + // coefficient to look up a multiple of. + // + // Skip trying to find the first nonzero coefficent, because + // searching might be more work than a few extra doublings. + for i := 255; i >= 0; i-- { + tmp1.Double(tmp2) + + for j := range nafs { + if nafs[j][i] > 0 { + v.fromP1xP1(tmp1) + tables[j].SelectInto(multiple, nafs[j][i]) + tmp1.Add(v, multiple) + } else if nafs[j][i] < 0 { + v.fromP1xP1(tmp1) + tables[j].SelectInto(multiple, -nafs[j][i]) + tmp1.Sub(v, multiple) + } + } + + tmp2.FromP1xP1(tmp1) + } + + v.fromP2(tmp2) + return v +} diff --git a/vendor/filippo.io/edwards25519/field/fe.go b/vendor/filippo.io/edwards25519/field/fe.go new file mode 100644 index 00000000..5518ef2b --- /dev/null +++ b/vendor/filippo.io/edwards25519/field/fe.go @@ -0,0 +1,420 @@ +// Copyright (c) 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package field implements fast arithmetic modulo 2^255-19. +package field + +import ( + "crypto/subtle" + "encoding/binary" + "errors" + "math/bits" +) + +// Element represents an element of the field GF(2^255-19). Note that this +// is not a cryptographically secure group, and should only be used to interact +// with edwards25519.Point coordinates. +// +// This type works similarly to math/big.Int, and all arguments and receivers +// are allowed to alias. +// +// The zero value is a valid zero element. +type Element struct { + // An element t represents the integer + // t.l0 + t.l1*2^51 + t.l2*2^102 + t.l3*2^153 + t.l4*2^204 + // + // Between operations, all limbs are expected to be lower than 2^52. + l0 uint64 + l1 uint64 + l2 uint64 + l3 uint64 + l4 uint64 +} + +const maskLow51Bits uint64 = (1 << 51) - 1 + +var feZero = &Element{0, 0, 0, 0, 0} + +// Zero sets v = 0, and returns v. +func (v *Element) Zero() *Element { + *v = *feZero + return v +} + +var feOne = &Element{1, 0, 0, 0, 0} + +// One sets v = 1, and returns v. +func (v *Element) One() *Element { + *v = *feOne + return v +} + +// reduce reduces v modulo 2^255 - 19 and returns it. +func (v *Element) reduce() *Element { + v.carryPropagate() + + // After the light reduction we now have a field element representation + // v < 2^255 + 2^13 * 19, but need v < 2^255 - 19. + + // If v >= 2^255 - 19, then v + 19 >= 2^255, which would overflow 2^255 - 1, + // generating a carry. That is, c will be 0 if v < 2^255 - 19, and 1 otherwise. + c := (v.l0 + 19) >> 51 + c = (v.l1 + c) >> 51 + c = (v.l2 + c) >> 51 + c = (v.l3 + c) >> 51 + c = (v.l4 + c) >> 51 + + // If v < 2^255 - 19 and c = 0, this will be a no-op. Otherwise, it's + // effectively applying the reduction identity to the carry. + v.l0 += 19 * c + + v.l1 += v.l0 >> 51 + v.l0 = v.l0 & maskLow51Bits + v.l2 += v.l1 >> 51 + v.l1 = v.l1 & maskLow51Bits + v.l3 += v.l2 >> 51 + v.l2 = v.l2 & maskLow51Bits + v.l4 += v.l3 >> 51 + v.l3 = v.l3 & maskLow51Bits + // no additional carry + v.l4 = v.l4 & maskLow51Bits + + return v +} + +// Add sets v = a + b, and returns v. +func (v *Element) Add(a, b *Element) *Element { + v.l0 = a.l0 + b.l0 + v.l1 = a.l1 + b.l1 + v.l2 = a.l2 + b.l2 + v.l3 = a.l3 + b.l3 + v.l4 = a.l4 + b.l4 + // Using the generic implementation here is actually faster than the + // assembly. Probably because the body of this function is so simple that + // the compiler can figure out better optimizations by inlining the carry + // propagation. + return v.carryPropagateGeneric() +} + +// Subtract sets v = a - b, and returns v. +func (v *Element) Subtract(a, b *Element) *Element { + // We first add 2 * p, to guarantee the subtraction won't underflow, and + // then subtract b (which can be up to 2^255 + 2^13 * 19). + v.l0 = (a.l0 + 0xFFFFFFFFFFFDA) - b.l0 + v.l1 = (a.l1 + 0xFFFFFFFFFFFFE) - b.l1 + v.l2 = (a.l2 + 0xFFFFFFFFFFFFE) - b.l2 + v.l3 = (a.l3 + 0xFFFFFFFFFFFFE) - b.l3 + v.l4 = (a.l4 + 0xFFFFFFFFFFFFE) - b.l4 + return v.carryPropagate() +} + +// Negate sets v = -a, and returns v. +func (v *Element) Negate(a *Element) *Element { + return v.Subtract(feZero, a) +} + +// Invert sets v = 1/z mod p, and returns v. +// +// If z == 0, Invert returns v = 0. +func (v *Element) Invert(z *Element) *Element { + // Inversion is implemented as exponentiation with exponent p − 2. It uses the + // same sequence of 255 squarings and 11 multiplications as [Curve25519]. + var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t Element + + z2.Square(z) // 2 + t.Square(&z2) // 4 + t.Square(&t) // 8 + z9.Multiply(&t, z) // 9 + z11.Multiply(&z9, &z2) // 11 + t.Square(&z11) // 22 + z2_5_0.Multiply(&t, &z9) // 31 = 2^5 - 2^0 + + t.Square(&z2_5_0) // 2^6 - 2^1 + for i := 0; i < 4; i++ { + t.Square(&t) // 2^10 - 2^5 + } + z2_10_0.Multiply(&t, &z2_5_0) // 2^10 - 2^0 + + t.Square(&z2_10_0) // 2^11 - 2^1 + for i := 0; i < 9; i++ { + t.Square(&t) // 2^20 - 2^10 + } + z2_20_0.Multiply(&t, &z2_10_0) // 2^20 - 2^0 + + t.Square(&z2_20_0) // 2^21 - 2^1 + for i := 0; i < 19; i++ { + t.Square(&t) // 2^40 - 2^20 + } + t.Multiply(&t, &z2_20_0) // 2^40 - 2^0 + + t.Square(&t) // 2^41 - 2^1 + for i := 0; i < 9; i++ { + t.Square(&t) // 2^50 - 2^10 + } + z2_50_0.Multiply(&t, &z2_10_0) // 2^50 - 2^0 + + t.Square(&z2_50_0) // 2^51 - 2^1 + for i := 0; i < 49; i++ { + t.Square(&t) // 2^100 - 2^50 + } + z2_100_0.Multiply(&t, &z2_50_0) // 2^100 - 2^0 + + t.Square(&z2_100_0) // 2^101 - 2^1 + for i := 0; i < 99; i++ { + t.Square(&t) // 2^200 - 2^100 + } + t.Multiply(&t, &z2_100_0) // 2^200 - 2^0 + + t.Square(&t) // 2^201 - 2^1 + for i := 0; i < 49; i++ { + t.Square(&t) // 2^250 - 2^50 + } + t.Multiply(&t, &z2_50_0) // 2^250 - 2^0 + + t.Square(&t) // 2^251 - 2^1 + t.Square(&t) // 2^252 - 2^2 + t.Square(&t) // 2^253 - 2^3 + t.Square(&t) // 2^254 - 2^4 + t.Square(&t) // 2^255 - 2^5 + + return v.Multiply(&t, &z11) // 2^255 - 21 +} + +// Set sets v = a, and returns v. +func (v *Element) Set(a *Element) *Element { + *v = *a + return v +} + +// SetBytes sets v to x, where x is a 32-byte little-endian encoding. If x is +// not of the right length, SetBytes returns nil and an error, and the +// receiver is unchanged. +// +// Consistent with RFC 7748, the most significant bit (the high bit of the +// last byte) is ignored, and non-canonical values (2^255-19 through 2^255-1) +// are accepted. Note that this is laxer than specified by RFC 8032, but +// consistent with most Ed25519 implementations. +func (v *Element) SetBytes(x []byte) (*Element, error) { + if len(x) != 32 { + return nil, errors.New("edwards25519: invalid field element input size") + } + + // Bits 0:51 (bytes 0:8, bits 0:64, shift 0, mask 51). + v.l0 = binary.LittleEndian.Uint64(x[0:8]) + v.l0 &= maskLow51Bits + // Bits 51:102 (bytes 6:14, bits 48:112, shift 3, mask 51). + v.l1 = binary.LittleEndian.Uint64(x[6:14]) >> 3 + v.l1 &= maskLow51Bits + // Bits 102:153 (bytes 12:20, bits 96:160, shift 6, mask 51). + v.l2 = binary.LittleEndian.Uint64(x[12:20]) >> 6 + v.l2 &= maskLow51Bits + // Bits 153:204 (bytes 19:27, bits 152:216, shift 1, mask 51). + v.l3 = binary.LittleEndian.Uint64(x[19:27]) >> 1 + v.l3 &= maskLow51Bits + // Bits 204:255 (bytes 24:32, bits 192:256, shift 12, mask 51). + // Note: not bytes 25:33, shift 4, to avoid overread. + v.l4 = binary.LittleEndian.Uint64(x[24:32]) >> 12 + v.l4 &= maskLow51Bits + + return v, nil +} + +// Bytes returns the canonical 32-byte little-endian encoding of v. +func (v *Element) Bytes() []byte { + // This function is outlined to make the allocations inline in the caller + // rather than happen on the heap. + var out [32]byte + return v.bytes(&out) +} + +func (v *Element) bytes(out *[32]byte) []byte { + t := *v + t.reduce() + + var buf [8]byte + for i, l := range [5]uint64{t.l0, t.l1, t.l2, t.l3, t.l4} { + bitsOffset := i * 51 + binary.LittleEndian.PutUint64(buf[:], l<= len(out) { + break + } + out[off] |= bb + } + } + + return out[:] +} + +// Equal returns 1 if v and u are equal, and 0 otherwise. +func (v *Element) Equal(u *Element) int { + sa, sv := u.Bytes(), v.Bytes() + return subtle.ConstantTimeCompare(sa, sv) +} + +// mask64Bits returns 0xffffffff if cond is 1, and 0 otherwise. +func mask64Bits(cond int) uint64 { return ^(uint64(cond) - 1) } + +// Select sets v to a if cond == 1, and to b if cond == 0. +func (v *Element) Select(a, b *Element, cond int) *Element { + m := mask64Bits(cond) + v.l0 = (m & a.l0) | (^m & b.l0) + v.l1 = (m & a.l1) | (^m & b.l1) + v.l2 = (m & a.l2) | (^m & b.l2) + v.l3 = (m & a.l3) | (^m & b.l3) + v.l4 = (m & a.l4) | (^m & b.l4) + return v +} + +// Swap swaps v and u if cond == 1 or leaves them unchanged if cond == 0, and returns v. +func (v *Element) Swap(u *Element, cond int) { + m := mask64Bits(cond) + t := m & (v.l0 ^ u.l0) + v.l0 ^= t + u.l0 ^= t + t = m & (v.l1 ^ u.l1) + v.l1 ^= t + u.l1 ^= t + t = m & (v.l2 ^ u.l2) + v.l2 ^= t + u.l2 ^= t + t = m & (v.l3 ^ u.l3) + v.l3 ^= t + u.l3 ^= t + t = m & (v.l4 ^ u.l4) + v.l4 ^= t + u.l4 ^= t +} + +// IsNegative returns 1 if v is negative, and 0 otherwise. +func (v *Element) IsNegative() int { + return int(v.Bytes()[0] & 1) +} + +// Absolute sets v to |u|, and returns v. +func (v *Element) Absolute(u *Element) *Element { + return v.Select(new(Element).Negate(u), u, u.IsNegative()) +} + +// Multiply sets v = x * y, and returns v. +func (v *Element) Multiply(x, y *Element) *Element { + feMul(v, x, y) + return v +} + +// Square sets v = x * x, and returns v. +func (v *Element) Square(x *Element) *Element { + feSquare(v, x) + return v +} + +// Mult32 sets v = x * y, and returns v. +func (v *Element) Mult32(x *Element, y uint32) *Element { + x0lo, x0hi := mul51(x.l0, y) + x1lo, x1hi := mul51(x.l1, y) + x2lo, x2hi := mul51(x.l2, y) + x3lo, x3hi := mul51(x.l3, y) + x4lo, x4hi := mul51(x.l4, y) + v.l0 = x0lo + 19*x4hi // carried over per the reduction identity + v.l1 = x1lo + x0hi + v.l2 = x2lo + x1hi + v.l3 = x3lo + x2hi + v.l4 = x4lo + x3hi + // The hi portions are going to be only 32 bits, plus any previous excess, + // so we can skip the carry propagation. + return v +} + +// mul51 returns lo + hi * 2⁵¹ = a * b. +func mul51(a uint64, b uint32) (lo uint64, hi uint64) { + mh, ml := bits.Mul64(a, uint64(b)) + lo = ml & maskLow51Bits + hi = (mh << 13) | (ml >> 51) + return +} + +// Pow22523 set v = x^((p-5)/8), and returns v. (p-5)/8 is 2^252-3. +func (v *Element) Pow22523(x *Element) *Element { + var t0, t1, t2 Element + + t0.Square(x) // x^2 + t1.Square(&t0) // x^4 + t1.Square(&t1) // x^8 + t1.Multiply(x, &t1) // x^9 + t0.Multiply(&t0, &t1) // x^11 + t0.Square(&t0) // x^22 + t0.Multiply(&t1, &t0) // x^31 + t1.Square(&t0) // x^62 + for i := 1; i < 5; i++ { // x^992 + t1.Square(&t1) + } + t0.Multiply(&t1, &t0) // x^1023 -> 1023 = 2^10 - 1 + t1.Square(&t0) // 2^11 - 2 + for i := 1; i < 10; i++ { // 2^20 - 2^10 + t1.Square(&t1) + } + t1.Multiply(&t1, &t0) // 2^20 - 1 + t2.Square(&t1) // 2^21 - 2 + for i := 1; i < 20; i++ { // 2^40 - 2^20 + t2.Square(&t2) + } + t1.Multiply(&t2, &t1) // 2^40 - 1 + t1.Square(&t1) // 2^41 - 2 + for i := 1; i < 10; i++ { // 2^50 - 2^10 + t1.Square(&t1) + } + t0.Multiply(&t1, &t0) // 2^50 - 1 + t1.Square(&t0) // 2^51 - 2 + for i := 1; i < 50; i++ { // 2^100 - 2^50 + t1.Square(&t1) + } + t1.Multiply(&t1, &t0) // 2^100 - 1 + t2.Square(&t1) // 2^101 - 2 + for i := 1; i < 100; i++ { // 2^200 - 2^100 + t2.Square(&t2) + } + t1.Multiply(&t2, &t1) // 2^200 - 1 + t1.Square(&t1) // 2^201 - 2 + for i := 1; i < 50; i++ { // 2^250 - 2^50 + t1.Square(&t1) + } + t0.Multiply(&t1, &t0) // 2^250 - 1 + t0.Square(&t0) // 2^251 - 2 + t0.Square(&t0) // 2^252 - 4 + return v.Multiply(&t0, x) // 2^252 - 3 -> x^(2^252-3) +} + +// sqrtM1 is 2^((p-1)/4), which squared is equal to -1 by Euler's Criterion. +var sqrtM1 = &Element{1718705420411056, 234908883556509, + 2233514472574048, 2117202627021982, 765476049583133} + +// SqrtRatio sets r to the non-negative square root of the ratio of u and v. +// +// If u/v is square, SqrtRatio returns r and 1. If u/v is not square, SqrtRatio +// sets r according to Section 4.3 of draft-irtf-cfrg-ristretto255-decaf448-00, +// and returns r and 0. +func (r *Element) SqrtRatio(u, v *Element) (R *Element, wasSquare int) { + t0 := new(Element) + + // r = (u * v3) * (u * v7)^((p-5)/8) + v2 := new(Element).Square(v) + uv3 := new(Element).Multiply(u, t0.Multiply(v2, v)) + uv7 := new(Element).Multiply(uv3, t0.Square(v2)) + rr := new(Element).Multiply(uv3, t0.Pow22523(uv7)) + + check := new(Element).Multiply(v, t0.Square(rr)) // check = v * r^2 + + uNeg := new(Element).Negate(u) + correctSignSqrt := check.Equal(u) + flippedSignSqrt := check.Equal(uNeg) + flippedSignSqrtI := check.Equal(t0.Multiply(uNeg, sqrtM1)) + + rPrime := new(Element).Multiply(rr, sqrtM1) // r_prime = SQRT_M1 * r + // r = CT_SELECT(r_prime IF flipped_sign_sqrt | flipped_sign_sqrt_i ELSE r) + rr.Select(rPrime, rr, flippedSignSqrt|flippedSignSqrtI) + + r.Absolute(rr) // Choose the nonnegative square root. + return r, correctSignSqrt | flippedSignSqrt +} diff --git a/vendor/filippo.io/edwards25519/field/fe_amd64.go b/vendor/filippo.io/edwards25519/field/fe_amd64.go new file mode 100644 index 00000000..edcf163c --- /dev/null +++ b/vendor/filippo.io/edwards25519/field/fe_amd64.go @@ -0,0 +1,16 @@ +// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT. + +//go:build amd64 && gc && !purego +// +build amd64,gc,!purego + +package field + +// feMul sets out = a * b. It works like feMulGeneric. +// +//go:noescape +func feMul(out *Element, a *Element, b *Element) + +// feSquare sets out = a * a. It works like feSquareGeneric. +// +//go:noescape +func feSquare(out *Element, a *Element) diff --git a/vendor/filippo.io/edwards25519/field/fe_amd64.s b/vendor/filippo.io/edwards25519/field/fe_amd64.s new file mode 100644 index 00000000..293f013c --- /dev/null +++ b/vendor/filippo.io/edwards25519/field/fe_amd64.s @@ -0,0 +1,379 @@ +// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT. + +//go:build amd64 && gc && !purego +// +build amd64,gc,!purego + +#include "textflag.h" + +// func feMul(out *Element, a *Element, b *Element) +TEXT ·feMul(SB), NOSPLIT, $0-24 + MOVQ a+8(FP), CX + MOVQ b+16(FP), BX + + // r0 = a0×b0 + MOVQ (CX), AX + MULQ (BX) + MOVQ AX, DI + MOVQ DX, SI + + // r0 += 19×a1×b4 + MOVQ 8(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 32(BX) + ADDQ AX, DI + ADCQ DX, SI + + // r0 += 19×a2×b3 + MOVQ 16(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 24(BX) + ADDQ AX, DI + ADCQ DX, SI + + // r0 += 19×a3×b2 + MOVQ 24(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 16(BX) + ADDQ AX, DI + ADCQ DX, SI + + // r0 += 19×a4×b1 + MOVQ 32(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 8(BX) + ADDQ AX, DI + ADCQ DX, SI + + // r1 = a0×b1 + MOVQ (CX), AX + MULQ 8(BX) + MOVQ AX, R9 + MOVQ DX, R8 + + // r1 += a1×b0 + MOVQ 8(CX), AX + MULQ (BX) + ADDQ AX, R9 + ADCQ DX, R8 + + // r1 += 19×a2×b4 + MOVQ 16(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 32(BX) + ADDQ AX, R9 + ADCQ DX, R8 + + // r1 += 19×a3×b3 + MOVQ 24(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 24(BX) + ADDQ AX, R9 + ADCQ DX, R8 + + // r1 += 19×a4×b2 + MOVQ 32(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 16(BX) + ADDQ AX, R9 + ADCQ DX, R8 + + // r2 = a0×b2 + MOVQ (CX), AX + MULQ 16(BX) + MOVQ AX, R11 + MOVQ DX, R10 + + // r2 += a1×b1 + MOVQ 8(CX), AX + MULQ 8(BX) + ADDQ AX, R11 + ADCQ DX, R10 + + // r2 += a2×b0 + MOVQ 16(CX), AX + MULQ (BX) + ADDQ AX, R11 + ADCQ DX, R10 + + // r2 += 19×a3×b4 + MOVQ 24(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 32(BX) + ADDQ AX, R11 + ADCQ DX, R10 + + // r2 += 19×a4×b3 + MOVQ 32(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 24(BX) + ADDQ AX, R11 + ADCQ DX, R10 + + // r3 = a0×b3 + MOVQ (CX), AX + MULQ 24(BX) + MOVQ AX, R13 + MOVQ DX, R12 + + // r3 += a1×b2 + MOVQ 8(CX), AX + MULQ 16(BX) + ADDQ AX, R13 + ADCQ DX, R12 + + // r3 += a2×b1 + MOVQ 16(CX), AX + MULQ 8(BX) + ADDQ AX, R13 + ADCQ DX, R12 + + // r3 += a3×b0 + MOVQ 24(CX), AX + MULQ (BX) + ADDQ AX, R13 + ADCQ DX, R12 + + // r3 += 19×a4×b4 + MOVQ 32(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 32(BX) + ADDQ AX, R13 + ADCQ DX, R12 + + // r4 = a0×b4 + MOVQ (CX), AX + MULQ 32(BX) + MOVQ AX, R15 + MOVQ DX, R14 + + // r4 += a1×b3 + MOVQ 8(CX), AX + MULQ 24(BX) + ADDQ AX, R15 + ADCQ DX, R14 + + // r4 += a2×b2 + MOVQ 16(CX), AX + MULQ 16(BX) + ADDQ AX, R15 + ADCQ DX, R14 + + // r4 += a3×b1 + MOVQ 24(CX), AX + MULQ 8(BX) + ADDQ AX, R15 + ADCQ DX, R14 + + // r4 += a4×b0 + MOVQ 32(CX), AX + MULQ (BX) + ADDQ AX, R15 + ADCQ DX, R14 + + // First reduction chain + MOVQ $0x0007ffffffffffff, AX + SHLQ $0x0d, DI, SI + SHLQ $0x0d, R9, R8 + SHLQ $0x0d, R11, R10 + SHLQ $0x0d, R13, R12 + SHLQ $0x0d, R15, R14 + ANDQ AX, DI + IMUL3Q $0x13, R14, R14 + ADDQ R14, DI + ANDQ AX, R9 + ADDQ SI, R9 + ANDQ AX, R11 + ADDQ R8, R11 + ANDQ AX, R13 + ADDQ R10, R13 + ANDQ AX, R15 + ADDQ R12, R15 + + // Second reduction chain (carryPropagate) + MOVQ DI, SI + SHRQ $0x33, SI + MOVQ R9, R8 + SHRQ $0x33, R8 + MOVQ R11, R10 + SHRQ $0x33, R10 + MOVQ R13, R12 + SHRQ $0x33, R12 + MOVQ R15, R14 + SHRQ $0x33, R14 + ANDQ AX, DI + IMUL3Q $0x13, R14, R14 + ADDQ R14, DI + ANDQ AX, R9 + ADDQ SI, R9 + ANDQ AX, R11 + ADDQ R8, R11 + ANDQ AX, R13 + ADDQ R10, R13 + ANDQ AX, R15 + ADDQ R12, R15 + + // Store output + MOVQ out+0(FP), AX + MOVQ DI, (AX) + MOVQ R9, 8(AX) + MOVQ R11, 16(AX) + MOVQ R13, 24(AX) + MOVQ R15, 32(AX) + RET + +// func feSquare(out *Element, a *Element) +TEXT ·feSquare(SB), NOSPLIT, $0-16 + MOVQ a+8(FP), CX + + // r0 = l0×l0 + MOVQ (CX), AX + MULQ (CX) + MOVQ AX, SI + MOVQ DX, BX + + // r0 += 38×l1×l4 + MOVQ 8(CX), AX + IMUL3Q $0x26, AX, AX + MULQ 32(CX) + ADDQ AX, SI + ADCQ DX, BX + + // r0 += 38×l2×l3 + MOVQ 16(CX), AX + IMUL3Q $0x26, AX, AX + MULQ 24(CX) + ADDQ AX, SI + ADCQ DX, BX + + // r1 = 2×l0×l1 + MOVQ (CX), AX + SHLQ $0x01, AX + MULQ 8(CX) + MOVQ AX, R8 + MOVQ DX, DI + + // r1 += 38×l2×l4 + MOVQ 16(CX), AX + IMUL3Q $0x26, AX, AX + MULQ 32(CX) + ADDQ AX, R8 + ADCQ DX, DI + + // r1 += 19×l3×l3 + MOVQ 24(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 24(CX) + ADDQ AX, R8 + ADCQ DX, DI + + // r2 = 2×l0×l2 + MOVQ (CX), AX + SHLQ $0x01, AX + MULQ 16(CX) + MOVQ AX, R10 + MOVQ DX, R9 + + // r2 += l1×l1 + MOVQ 8(CX), AX + MULQ 8(CX) + ADDQ AX, R10 + ADCQ DX, R9 + + // r2 += 38×l3×l4 + MOVQ 24(CX), AX + IMUL3Q $0x26, AX, AX + MULQ 32(CX) + ADDQ AX, R10 + ADCQ DX, R9 + + // r3 = 2×l0×l3 + MOVQ (CX), AX + SHLQ $0x01, AX + MULQ 24(CX) + MOVQ AX, R12 + MOVQ DX, R11 + + // r3 += 2×l1×l2 + MOVQ 8(CX), AX + IMUL3Q $0x02, AX, AX + MULQ 16(CX) + ADDQ AX, R12 + ADCQ DX, R11 + + // r3 += 19×l4×l4 + MOVQ 32(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 32(CX) + ADDQ AX, R12 + ADCQ DX, R11 + + // r4 = 2×l0×l4 + MOVQ (CX), AX + SHLQ $0x01, AX + MULQ 32(CX) + MOVQ AX, R14 + MOVQ DX, R13 + + // r4 += 2×l1×l3 + MOVQ 8(CX), AX + IMUL3Q $0x02, AX, AX + MULQ 24(CX) + ADDQ AX, R14 + ADCQ DX, R13 + + // r4 += l2×l2 + MOVQ 16(CX), AX + MULQ 16(CX) + ADDQ AX, R14 + ADCQ DX, R13 + + // First reduction chain + MOVQ $0x0007ffffffffffff, AX + SHLQ $0x0d, SI, BX + SHLQ $0x0d, R8, DI + SHLQ $0x0d, R10, R9 + SHLQ $0x0d, R12, R11 + SHLQ $0x0d, R14, R13 + ANDQ AX, SI + IMUL3Q $0x13, R13, R13 + ADDQ R13, SI + ANDQ AX, R8 + ADDQ BX, R8 + ANDQ AX, R10 + ADDQ DI, R10 + ANDQ AX, R12 + ADDQ R9, R12 + ANDQ AX, R14 + ADDQ R11, R14 + + // Second reduction chain (carryPropagate) + MOVQ SI, BX + SHRQ $0x33, BX + MOVQ R8, DI + SHRQ $0x33, DI + MOVQ R10, R9 + SHRQ $0x33, R9 + MOVQ R12, R11 + SHRQ $0x33, R11 + MOVQ R14, R13 + SHRQ $0x33, R13 + ANDQ AX, SI + IMUL3Q $0x13, R13, R13 + ADDQ R13, SI + ANDQ AX, R8 + ADDQ BX, R8 + ANDQ AX, R10 + ADDQ DI, R10 + ANDQ AX, R12 + ADDQ R9, R12 + ANDQ AX, R14 + ADDQ R11, R14 + + // Store output + MOVQ out+0(FP), AX + MOVQ SI, (AX) + MOVQ R8, 8(AX) + MOVQ R10, 16(AX) + MOVQ R12, 24(AX) + MOVQ R14, 32(AX) + RET diff --git a/vendor/filippo.io/edwards25519/field/fe_amd64_noasm.go b/vendor/filippo.io/edwards25519/field/fe_amd64_noasm.go new file mode 100644 index 00000000..ddb6c9b8 --- /dev/null +++ b/vendor/filippo.io/edwards25519/field/fe_amd64_noasm.go @@ -0,0 +1,12 @@ +// Copyright (c) 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !amd64 || !gc || purego +// +build !amd64 !gc purego + +package field + +func feMul(v, x, y *Element) { feMulGeneric(v, x, y) } + +func feSquare(v, x *Element) { feSquareGeneric(v, x) } diff --git a/vendor/filippo.io/edwards25519/field/fe_arm64.go b/vendor/filippo.io/edwards25519/field/fe_arm64.go new file mode 100644 index 00000000..af459ef5 --- /dev/null +++ b/vendor/filippo.io/edwards25519/field/fe_arm64.go @@ -0,0 +1,16 @@ +// Copyright (c) 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build arm64 && gc && !purego +// +build arm64,gc,!purego + +package field + +//go:noescape +func carryPropagate(v *Element) + +func (v *Element) carryPropagate() *Element { + carryPropagate(v) + return v +} diff --git a/vendor/filippo.io/edwards25519/field/fe_arm64.s b/vendor/filippo.io/edwards25519/field/fe_arm64.s new file mode 100644 index 00000000..3126a434 --- /dev/null +++ b/vendor/filippo.io/edwards25519/field/fe_arm64.s @@ -0,0 +1,42 @@ +// Copyright (c) 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build arm64 && gc && !purego + +#include "textflag.h" + +// carryPropagate works exactly like carryPropagateGeneric and uses the +// same AND, ADD, and LSR+MADD instructions emitted by the compiler, but +// avoids loading R0-R4 twice and uses LDP and STP. +// +// See https://golang.org/issues/43145 for the main compiler issue. +// +// func carryPropagate(v *Element) +TEXT ·carryPropagate(SB),NOFRAME|NOSPLIT,$0-8 + MOVD v+0(FP), R20 + + LDP 0(R20), (R0, R1) + LDP 16(R20), (R2, R3) + MOVD 32(R20), R4 + + AND $0x7ffffffffffff, R0, R10 + AND $0x7ffffffffffff, R1, R11 + AND $0x7ffffffffffff, R2, R12 + AND $0x7ffffffffffff, R3, R13 + AND $0x7ffffffffffff, R4, R14 + + ADD R0>>51, R11, R11 + ADD R1>>51, R12, R12 + ADD R2>>51, R13, R13 + ADD R3>>51, R14, R14 + // R4>>51 * 19 + R10 -> R10 + LSR $51, R4, R21 + MOVD $19, R22 + MADD R22, R10, R21, R10 + + STP (R10, R11), 0(R20) + STP (R12, R13), 16(R20) + MOVD R14, 32(R20) + + RET diff --git a/vendor/filippo.io/edwards25519/field/fe_arm64_noasm.go b/vendor/filippo.io/edwards25519/field/fe_arm64_noasm.go new file mode 100644 index 00000000..234a5b2e --- /dev/null +++ b/vendor/filippo.io/edwards25519/field/fe_arm64_noasm.go @@ -0,0 +1,12 @@ +// Copyright (c) 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !arm64 || !gc || purego +// +build !arm64 !gc purego + +package field + +func (v *Element) carryPropagate() *Element { + return v.carryPropagateGeneric() +} diff --git a/vendor/filippo.io/edwards25519/field/fe_extra.go b/vendor/filippo.io/edwards25519/field/fe_extra.go new file mode 100644 index 00000000..1ef503b9 --- /dev/null +++ b/vendor/filippo.io/edwards25519/field/fe_extra.go @@ -0,0 +1,50 @@ +// Copyright (c) 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package field + +import "errors" + +// This file contains additional functionality that is not included in the +// upstream crypto/ed25519/edwards25519/field package. + +// SetWideBytes sets v to x, where x is a 64-byte little-endian encoding, which +// is reduced modulo the field order. If x is not of the right length, +// SetWideBytes returns nil and an error, and the receiver is unchanged. +// +// SetWideBytes is not necessary to select a uniformly distributed value, and is +// only provided for compatibility: SetBytes can be used instead as the chance +// of bias is less than 2⁻²⁵⁰. +func (v *Element) SetWideBytes(x []byte) (*Element, error) { + if len(x) != 64 { + return nil, errors.New("edwards25519: invalid SetWideBytes input size") + } + + // Split the 64 bytes into two elements, and extract the most significant + // bit of each, which is ignored by SetBytes. + lo, _ := new(Element).SetBytes(x[:32]) + loMSB := uint64(x[31] >> 7) + hi, _ := new(Element).SetBytes(x[32:]) + hiMSB := uint64(x[63] >> 7) + + // The output we want is + // + // v = lo + loMSB * 2²⁵⁵ + hi * 2²⁵⁶ + hiMSB * 2⁵¹¹ + // + // which applying the reduction identity comes out to + // + // v = lo + loMSB * 19 + hi * 2 * 19 + hiMSB * 2 * 19² + // + // l0 will be the sum of a 52 bits value (lo.l0), plus a 5 bits value + // (loMSB * 19), a 6 bits value (hi.l0 * 2 * 19), and a 10 bits value + // (hiMSB * 2 * 19²), so it fits in a uint64. + + v.l0 = lo.l0 + loMSB*19 + hi.l0*2*19 + hiMSB*2*19*19 + v.l1 = lo.l1 + hi.l1*2*19 + v.l2 = lo.l2 + hi.l2*2*19 + v.l3 = lo.l3 + hi.l3*2*19 + v.l4 = lo.l4 + hi.l4*2*19 + + return v.carryPropagate(), nil +} diff --git a/vendor/filippo.io/edwards25519/field/fe_generic.go b/vendor/filippo.io/edwards25519/field/fe_generic.go new file mode 100644 index 00000000..86f5fd95 --- /dev/null +++ b/vendor/filippo.io/edwards25519/field/fe_generic.go @@ -0,0 +1,266 @@ +// Copyright (c) 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package field + +import "math/bits" + +// uint128 holds a 128-bit number as two 64-bit limbs, for use with the +// bits.Mul64 and bits.Add64 intrinsics. +type uint128 struct { + lo, hi uint64 +} + +// mul64 returns a * b. +func mul64(a, b uint64) uint128 { + hi, lo := bits.Mul64(a, b) + return uint128{lo, hi} +} + +// addMul64 returns v + a * b. +func addMul64(v uint128, a, b uint64) uint128 { + hi, lo := bits.Mul64(a, b) + lo, c := bits.Add64(lo, v.lo, 0) + hi, _ = bits.Add64(hi, v.hi, c) + return uint128{lo, hi} +} + +// shiftRightBy51 returns a >> 51. a is assumed to be at most 115 bits. +func shiftRightBy51(a uint128) uint64 { + return (a.hi << (64 - 51)) | (a.lo >> 51) +} + +func feMulGeneric(v, a, b *Element) { + a0 := a.l0 + a1 := a.l1 + a2 := a.l2 + a3 := a.l3 + a4 := a.l4 + + b0 := b.l0 + b1 := b.l1 + b2 := b.l2 + b3 := b.l3 + b4 := b.l4 + + // Limb multiplication works like pen-and-paper columnar multiplication, but + // with 51-bit limbs instead of digits. + // + // a4 a3 a2 a1 a0 x + // b4 b3 b2 b1 b0 = + // ------------------------ + // a4b0 a3b0 a2b0 a1b0 a0b0 + + // a4b1 a3b1 a2b1 a1b1 a0b1 + + // a4b2 a3b2 a2b2 a1b2 a0b2 + + // a4b3 a3b3 a2b3 a1b3 a0b3 + + // a4b4 a3b4 a2b4 a1b4 a0b4 = + // ---------------------------------------------- + // r8 r7 r6 r5 r4 r3 r2 r1 r0 + // + // We can then use the reduction identity (a * 2²⁵⁵ + b = a * 19 + b) to + // reduce the limbs that would overflow 255 bits. r5 * 2²⁵⁵ becomes 19 * r5, + // r6 * 2³⁰⁶ becomes 19 * r6 * 2⁵¹, etc. + // + // Reduction can be carried out simultaneously to multiplication. For + // example, we do not compute r5: whenever the result of a multiplication + // belongs to r5, like a1b4, we multiply it by 19 and add the result to r0. + // + // a4b0 a3b0 a2b0 a1b0 a0b0 + + // a3b1 a2b1 a1b1 a0b1 19×a4b1 + + // a2b2 a1b2 a0b2 19×a4b2 19×a3b2 + + // a1b3 a0b3 19×a4b3 19×a3b3 19×a2b3 + + // a0b4 19×a4b4 19×a3b4 19×a2b4 19×a1b4 = + // -------------------------------------- + // r4 r3 r2 r1 r0 + // + // Finally we add up the columns into wide, overlapping limbs. + + a1_19 := a1 * 19 + a2_19 := a2 * 19 + a3_19 := a3 * 19 + a4_19 := a4 * 19 + + // r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1) + r0 := mul64(a0, b0) + r0 = addMul64(r0, a1_19, b4) + r0 = addMul64(r0, a2_19, b3) + r0 = addMul64(r0, a3_19, b2) + r0 = addMul64(r0, a4_19, b1) + + // r1 = a0×b1 + a1×b0 + 19×(a2×b4 + a3×b3 + a4×b2) + r1 := mul64(a0, b1) + r1 = addMul64(r1, a1, b0) + r1 = addMul64(r1, a2_19, b4) + r1 = addMul64(r1, a3_19, b3) + r1 = addMul64(r1, a4_19, b2) + + // r2 = a0×b2 + a1×b1 + a2×b0 + 19×(a3×b4 + a4×b3) + r2 := mul64(a0, b2) + r2 = addMul64(r2, a1, b1) + r2 = addMul64(r2, a2, b0) + r2 = addMul64(r2, a3_19, b4) + r2 = addMul64(r2, a4_19, b3) + + // r3 = a0×b3 + a1×b2 + a2×b1 + a3×b0 + 19×a4×b4 + r3 := mul64(a0, b3) + r3 = addMul64(r3, a1, b2) + r3 = addMul64(r3, a2, b1) + r3 = addMul64(r3, a3, b0) + r3 = addMul64(r3, a4_19, b4) + + // r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0 + r4 := mul64(a0, b4) + r4 = addMul64(r4, a1, b3) + r4 = addMul64(r4, a2, b2) + r4 = addMul64(r4, a3, b1) + r4 = addMul64(r4, a4, b0) + + // After the multiplication, we need to reduce (carry) the five coefficients + // to obtain a result with limbs that are at most slightly larger than 2⁵¹, + // to respect the Element invariant. + // + // Overall, the reduction works the same as carryPropagate, except with + // wider inputs: we take the carry for each coefficient by shifting it right + // by 51, and add it to the limb above it. The top carry is multiplied by 19 + // according to the reduction identity and added to the lowest limb. + // + // The largest coefficient (r0) will be at most 111 bits, which guarantees + // that all carries are at most 111 - 51 = 60 bits, which fits in a uint64. + // + // r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1) + // r0 < 2⁵²×2⁵² + 19×(2⁵²×2⁵² + 2⁵²×2⁵² + 2⁵²×2⁵² + 2⁵²×2⁵²) + // r0 < (1 + 19 × 4) × 2⁵² × 2⁵² + // r0 < 2⁷ × 2⁵² × 2⁵² + // r0 < 2¹¹¹ + // + // Moreover, the top coefficient (r4) is at most 107 bits, so c4 is at most + // 56 bits, and c4 * 19 is at most 61 bits, which again fits in a uint64 and + // allows us to easily apply the reduction identity. + // + // r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0 + // r4 < 5 × 2⁵² × 2⁵² + // r4 < 2¹⁰⁷ + // + + c0 := shiftRightBy51(r0) + c1 := shiftRightBy51(r1) + c2 := shiftRightBy51(r2) + c3 := shiftRightBy51(r3) + c4 := shiftRightBy51(r4) + + rr0 := r0.lo&maskLow51Bits + c4*19 + rr1 := r1.lo&maskLow51Bits + c0 + rr2 := r2.lo&maskLow51Bits + c1 + rr3 := r3.lo&maskLow51Bits + c2 + rr4 := r4.lo&maskLow51Bits + c3 + + // Now all coefficients fit into 64-bit registers but are still too large to + // be passed around as an Element. We therefore do one last carry chain, + // where the carries will be small enough to fit in the wiggle room above 2⁵¹. + *v = Element{rr0, rr1, rr2, rr3, rr4} + v.carryPropagate() +} + +func feSquareGeneric(v, a *Element) { + l0 := a.l0 + l1 := a.l1 + l2 := a.l2 + l3 := a.l3 + l4 := a.l4 + + // Squaring works precisely like multiplication above, but thanks to its + // symmetry we get to group a few terms together. + // + // l4 l3 l2 l1 l0 x + // l4 l3 l2 l1 l0 = + // ------------------------ + // l4l0 l3l0 l2l0 l1l0 l0l0 + + // l4l1 l3l1 l2l1 l1l1 l0l1 + + // l4l2 l3l2 l2l2 l1l2 l0l2 + + // l4l3 l3l3 l2l3 l1l3 l0l3 + + // l4l4 l3l4 l2l4 l1l4 l0l4 = + // ---------------------------------------------- + // r8 r7 r6 r5 r4 r3 r2 r1 r0 + // + // l4l0 l3l0 l2l0 l1l0 l0l0 + + // l3l1 l2l1 l1l1 l0l1 19×l4l1 + + // l2l2 l1l2 l0l2 19×l4l2 19×l3l2 + + // l1l3 l0l3 19×l4l3 19×l3l3 19×l2l3 + + // l0l4 19×l4l4 19×l3l4 19×l2l4 19×l1l4 = + // -------------------------------------- + // r4 r3 r2 r1 r0 + // + // With precomputed 2×, 19×, and 2×19× terms, we can compute each limb with + // only three Mul64 and four Add64, instead of five and eight. + + l0_2 := l0 * 2 + l1_2 := l1 * 2 + + l1_38 := l1 * 38 + l2_38 := l2 * 38 + l3_38 := l3 * 38 + + l3_19 := l3 * 19 + l4_19 := l4 * 19 + + // r0 = l0×l0 + 19×(l1×l4 + l2×l3 + l3×l2 + l4×l1) = l0×l0 + 19×2×(l1×l4 + l2×l3) + r0 := mul64(l0, l0) + r0 = addMul64(r0, l1_38, l4) + r0 = addMul64(r0, l2_38, l3) + + // r1 = l0×l1 + l1×l0 + 19×(l2×l4 + l3×l3 + l4×l2) = 2×l0×l1 + 19×2×l2×l4 + 19×l3×l3 + r1 := mul64(l0_2, l1) + r1 = addMul64(r1, l2_38, l4) + r1 = addMul64(r1, l3_19, l3) + + // r2 = l0×l2 + l1×l1 + l2×l0 + 19×(l3×l4 + l4×l3) = 2×l0×l2 + l1×l1 + 19×2×l3×l4 + r2 := mul64(l0_2, l2) + r2 = addMul64(r2, l1, l1) + r2 = addMul64(r2, l3_38, l4) + + // r3 = l0×l3 + l1×l2 + l2×l1 + l3×l0 + 19×l4×l4 = 2×l0×l3 + 2×l1×l2 + 19×l4×l4 + r3 := mul64(l0_2, l3) + r3 = addMul64(r3, l1_2, l2) + r3 = addMul64(r3, l4_19, l4) + + // r4 = l0×l4 + l1×l3 + l2×l2 + l3×l1 + l4×l0 = 2×l0×l4 + 2×l1×l3 + l2×l2 + r4 := mul64(l0_2, l4) + r4 = addMul64(r4, l1_2, l3) + r4 = addMul64(r4, l2, l2) + + c0 := shiftRightBy51(r0) + c1 := shiftRightBy51(r1) + c2 := shiftRightBy51(r2) + c3 := shiftRightBy51(r3) + c4 := shiftRightBy51(r4) + + rr0 := r0.lo&maskLow51Bits + c4*19 + rr1 := r1.lo&maskLow51Bits + c0 + rr2 := r2.lo&maskLow51Bits + c1 + rr3 := r3.lo&maskLow51Bits + c2 + rr4 := r4.lo&maskLow51Bits + c3 + + *v = Element{rr0, rr1, rr2, rr3, rr4} + v.carryPropagate() +} + +// carryPropagateGeneric brings the limbs below 52 bits by applying the reduction +// identity (a * 2²⁵⁵ + b = a * 19 + b) to the l4 carry. +func (v *Element) carryPropagateGeneric() *Element { + c0 := v.l0 >> 51 + c1 := v.l1 >> 51 + c2 := v.l2 >> 51 + c3 := v.l3 >> 51 + c4 := v.l4 >> 51 + + // c4 is at most 64 - 51 = 13 bits, so c4*19 is at most 18 bits, and + // the final l0 will be at most 52 bits. Similarly for the rest. + v.l0 = v.l0&maskLow51Bits + c4*19 + v.l1 = v.l1&maskLow51Bits + c0 + v.l2 = v.l2&maskLow51Bits + c1 + v.l3 = v.l3&maskLow51Bits + c2 + v.l4 = v.l4&maskLow51Bits + c3 + + return v +} diff --git a/vendor/filippo.io/edwards25519/scalar.go b/vendor/filippo.io/edwards25519/scalar.go new file mode 100644 index 00000000..3fd16538 --- /dev/null +++ b/vendor/filippo.io/edwards25519/scalar.go @@ -0,0 +1,343 @@ +// Copyright (c) 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package edwards25519 + +import ( + "encoding/binary" + "errors" +) + +// A Scalar is an integer modulo +// +// l = 2^252 + 27742317777372353535851937790883648493 +// +// which is the prime order of the edwards25519 group. +// +// This type works similarly to math/big.Int, and all arguments and +// receivers are allowed to alias. +// +// The zero value is a valid zero element. +type Scalar struct { + // s is the scalar in the Montgomery domain, in the format of the + // fiat-crypto implementation. + s fiatScalarMontgomeryDomainFieldElement +} + +// The field implementation in scalar_fiat.go is generated by the fiat-crypto +// project (https://github.com/mit-plv/fiat-crypto) at version v0.0.9 (23d2dbc) +// from a formally verified model. +// +// fiat-crypto code comes under the following license. +// +// Copyright (c) 2015-2020 The fiat-crypto Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// THIS SOFTWARE IS PROVIDED BY the fiat-crypto authors "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, +// Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +// NewScalar returns a new zero Scalar. +func NewScalar() *Scalar { + return &Scalar{} +} + +// MultiplyAdd sets s = x * y + z mod l, and returns s. It is equivalent to +// using Multiply and then Add. +func (s *Scalar) MultiplyAdd(x, y, z *Scalar) *Scalar { + // Make a copy of z in case it aliases s. + zCopy := new(Scalar).Set(z) + return s.Multiply(x, y).Add(s, zCopy) +} + +// Add sets s = x + y mod l, and returns s. +func (s *Scalar) Add(x, y *Scalar) *Scalar { + // s = 1 * x + y mod l + fiatScalarAdd(&s.s, &x.s, &y.s) + return s +} + +// Subtract sets s = x - y mod l, and returns s. +func (s *Scalar) Subtract(x, y *Scalar) *Scalar { + // s = -1 * y + x mod l + fiatScalarSub(&s.s, &x.s, &y.s) + return s +} + +// Negate sets s = -x mod l, and returns s. +func (s *Scalar) Negate(x *Scalar) *Scalar { + // s = -1 * x + 0 mod l + fiatScalarOpp(&s.s, &x.s) + return s +} + +// Multiply sets s = x * y mod l, and returns s. +func (s *Scalar) Multiply(x, y *Scalar) *Scalar { + // s = x * y + 0 mod l + fiatScalarMul(&s.s, &x.s, &y.s) + return s +} + +// Set sets s = x, and returns s. +func (s *Scalar) Set(x *Scalar) *Scalar { + *s = *x + return s +} + +// SetUniformBytes sets s = x mod l, where x is a 64-byte little-endian integer. +// If x is not of the right length, SetUniformBytes returns nil and an error, +// and the receiver is unchanged. +// +// SetUniformBytes can be used to set s to a uniformly distributed value given +// 64 uniformly distributed random bytes. +func (s *Scalar) SetUniformBytes(x []byte) (*Scalar, error) { + if len(x) != 64 { + return nil, errors.New("edwards25519: invalid SetUniformBytes input length") + } + + // We have a value x of 512 bits, but our fiatScalarFromBytes function + // expects an input lower than l, which is a little over 252 bits. + // + // Instead of writing a reduction function that operates on wider inputs, we + // can interpret x as the sum of three shorter values a, b, and c. + // + // x = a + b * 2^168 + c * 2^336 mod l + // + // We then precompute 2^168 and 2^336 modulo l, and perform the reduction + // with two multiplications and two additions. + + s.setShortBytes(x[:21]) + t := new(Scalar).setShortBytes(x[21:42]) + s.Add(s, t.Multiply(t, scalarTwo168)) + t.setShortBytes(x[42:]) + s.Add(s, t.Multiply(t, scalarTwo336)) + + return s, nil +} + +// scalarTwo168 and scalarTwo336 are 2^168 and 2^336 modulo l, encoded as a +// fiatScalarMontgomeryDomainFieldElement, which is a little-endian 4-limb value +// in the 2^256 Montgomery domain. +var scalarTwo168 = &Scalar{s: [4]uint64{0x5b8ab432eac74798, 0x38afddd6de59d5d7, + 0xa2c131b399411b7c, 0x6329a7ed9ce5a30}} +var scalarTwo336 = &Scalar{s: [4]uint64{0xbd3d108e2b35ecc5, 0x5c3a3718bdf9c90b, + 0x63aa97a331b4f2ee, 0x3d217f5be65cb5c}} + +// setShortBytes sets s = x mod l, where x is a little-endian integer shorter +// than 32 bytes. +func (s *Scalar) setShortBytes(x []byte) *Scalar { + if len(x) >= 32 { + panic("edwards25519: internal error: setShortBytes called with a long string") + } + var buf [32]byte + copy(buf[:], x) + fiatScalarFromBytes((*[4]uint64)(&s.s), &buf) + fiatScalarToMontgomery(&s.s, (*fiatScalarNonMontgomeryDomainFieldElement)(&s.s)) + return s +} + +// SetCanonicalBytes sets s = x, where x is a 32-byte little-endian encoding of +// s, and returns s. If x is not a canonical encoding of s, SetCanonicalBytes +// returns nil and an error, and the receiver is unchanged. +func (s *Scalar) SetCanonicalBytes(x []byte) (*Scalar, error) { + if len(x) != 32 { + return nil, errors.New("invalid scalar length") + } + if !isReduced(x) { + return nil, errors.New("invalid scalar encoding") + } + + fiatScalarFromBytes((*[4]uint64)(&s.s), (*[32]byte)(x)) + fiatScalarToMontgomery(&s.s, (*fiatScalarNonMontgomeryDomainFieldElement)(&s.s)) + + return s, nil +} + +// scalarMinusOneBytes is l - 1 in little endian. +var scalarMinusOneBytes = [32]byte{236, 211, 245, 92, 26, 99, 18, 88, 214, 156, 247, 162, 222, 249, 222, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16} + +// isReduced returns whether the given scalar in 32-byte little endian encoded +// form is reduced modulo l. +func isReduced(s []byte) bool { + if len(s) != 32 { + return false + } + + for i := len(s) - 1; i >= 0; i-- { + switch { + case s[i] > scalarMinusOneBytes[i]: + return false + case s[i] < scalarMinusOneBytes[i]: + return true + } + } + return true +} + +// SetBytesWithClamping applies the buffer pruning described in RFC 8032, +// Section 5.1.5 (also known as clamping) and sets s to the result. The input +// must be 32 bytes, and it is not modified. If x is not of the right length, +// SetBytesWithClamping returns nil and an error, and the receiver is unchanged. +// +// Note that since Scalar values are always reduced modulo the prime order of +// the curve, the resulting value will not preserve any of the cofactor-clearing +// properties that clamping is meant to provide. It will however work as +// expected as long as it is applied to points on the prime order subgroup, like +// in Ed25519. In fact, it is lost to history why RFC 8032 adopted the +// irrelevant RFC 7748 clamping, but it is now required for compatibility. +func (s *Scalar) SetBytesWithClamping(x []byte) (*Scalar, error) { + // The description above omits the purpose of the high bits of the clamping + // for brevity, but those are also lost to reductions, and are also + // irrelevant to edwards25519 as they protect against a specific + // implementation bug that was once observed in a generic Montgomery ladder. + if len(x) != 32 { + return nil, errors.New("edwards25519: invalid SetBytesWithClamping input length") + } + + // We need to use the wide reduction from SetUniformBytes, since clamping + // sets the 2^254 bit, making the value higher than the order. + var wideBytes [64]byte + copy(wideBytes[:], x[:]) + wideBytes[0] &= 248 + wideBytes[31] &= 63 + wideBytes[31] |= 64 + return s.SetUniformBytes(wideBytes[:]) +} + +// Bytes returns the canonical 32-byte little-endian encoding of s. +func (s *Scalar) Bytes() []byte { + // This function is outlined to make the allocations inline in the caller + // rather than happen on the heap. + var encoded [32]byte + return s.bytes(&encoded) +} + +func (s *Scalar) bytes(out *[32]byte) []byte { + var ss fiatScalarNonMontgomeryDomainFieldElement + fiatScalarFromMontgomery(&ss, &s.s) + fiatScalarToBytes(out, (*[4]uint64)(&ss)) + return out[:] +} + +// Equal returns 1 if s and t are equal, and 0 otherwise. +func (s *Scalar) Equal(t *Scalar) int { + var diff fiatScalarMontgomeryDomainFieldElement + fiatScalarSub(&diff, &s.s, &t.s) + var nonzero uint64 + fiatScalarNonzero(&nonzero, (*[4]uint64)(&diff)) + nonzero |= nonzero >> 32 + nonzero |= nonzero >> 16 + nonzero |= nonzero >> 8 + nonzero |= nonzero >> 4 + nonzero |= nonzero >> 2 + nonzero |= nonzero >> 1 + return int(^nonzero) & 1 +} + +// nonAdjacentForm computes a width-w non-adjacent form for this scalar. +// +// w must be between 2 and 8, or nonAdjacentForm will panic. +func (s *Scalar) nonAdjacentForm(w uint) [256]int8 { + // This implementation is adapted from the one + // in curve25519-dalek and is documented there: + // https://github.com/dalek-cryptography/curve25519-dalek/blob/f630041af28e9a405255f98a8a93adca18e4315b/src/scalar.rs#L800-L871 + b := s.Bytes() + if b[31] > 127 { + panic("scalar has high bit set illegally") + } + if w < 2 { + panic("w must be at least 2 by the definition of NAF") + } else if w > 8 { + panic("NAF digits must fit in int8") + } + + var naf [256]int8 + var digits [5]uint64 + + for i := 0; i < 4; i++ { + digits[i] = binary.LittleEndian.Uint64(b[i*8:]) + } + + width := uint64(1 << w) + windowMask := uint64(width - 1) + + pos := uint(0) + carry := uint64(0) + for pos < 256 { + indexU64 := pos / 64 + indexBit := pos % 64 + var bitBuf uint64 + if indexBit < 64-w { + // This window's bits are contained in a single u64 + bitBuf = digits[indexU64] >> indexBit + } else { + // Combine the current 64 bits with bits from the next 64 + bitBuf = (digits[indexU64] >> indexBit) | (digits[1+indexU64] << (64 - indexBit)) + } + + // Add carry into the current window + window := carry + (bitBuf & windowMask) + + if window&1 == 0 { + // If the window value is even, preserve the carry and continue. + // Why is the carry preserved? + // If carry == 0 and window & 1 == 0, + // then the next carry should be 0 + // If carry == 1 and window & 1 == 0, + // then bit_buf & 1 == 1 so the next carry should be 1 + pos += 1 + continue + } + + if window < width/2 { + carry = 0 + naf[pos] = int8(window) + } else { + carry = 1 + naf[pos] = int8(window) - int8(width) + } + + pos += w + } + return naf +} + +func (s *Scalar) signedRadix16() [64]int8 { + b := s.Bytes() + if b[31] > 127 { + panic("scalar has high bit set illegally") + } + + var digits [64]int8 + + // Compute unsigned radix-16 digits: + for i := 0; i < 32; i++ { + digits[2*i] = int8(b[i] & 15) + digits[2*i+1] = int8((b[i] >> 4) & 15) + } + + // Recenter coefficients: + for i := 0; i < 63; i++ { + carry := (digits[i] + 8) >> 4 + digits[i] -= carry << 4 + digits[i+1] += carry + } + + return digits +} diff --git a/vendor/filippo.io/edwards25519/scalar_fiat.go b/vendor/filippo.io/edwards25519/scalar_fiat.go new file mode 100644 index 00000000..2e5782b6 --- /dev/null +++ b/vendor/filippo.io/edwards25519/scalar_fiat.go @@ -0,0 +1,1147 @@ +// Code generated by Fiat Cryptography. DO NOT EDIT. +// +// Autogenerated: word_by_word_montgomery --lang Go --cmovznz-by-mul --relax-primitive-carry-to-bitwidth 32,64 --public-function-case camelCase --public-type-case camelCase --private-function-case camelCase --private-type-case camelCase --doc-text-before-function-name '' --doc-newline-before-package-declaration --doc-prepend-header 'Code generated by Fiat Cryptography. DO NOT EDIT.' --package-name edwards25519 Scalar 64 '2^252 + 27742317777372353535851937790883648493' mul add sub opp nonzero from_montgomery to_montgomery to_bytes from_bytes +// +// curve description: Scalar +// +// machine_wordsize = 64 (from "64") +// +// requested operations: mul, add, sub, opp, nonzero, from_montgomery, to_montgomery, to_bytes, from_bytes +// +// m = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed (from "2^252 + 27742317777372353535851937790883648493") +// +// +// +// NOTE: In addition to the bounds specified above each function, all +// +// functions synthesized for this Montgomery arithmetic require the +// +// input to be strictly less than the prime modulus (m), and also +// +// require the input to be in the unique saturated representation. +// +// All functions also ensure that these two properties are true of +// +// return values. +// +// +// +// Computed values: +// +// eval z = z[0] + (z[1] << 64) + (z[2] << 128) + (z[3] << 192) +// +// bytes_eval z = z[0] + (z[1] << 8) + (z[2] << 16) + (z[3] << 24) + (z[4] << 32) + (z[5] << 40) + (z[6] << 48) + (z[7] << 56) + (z[8] << 64) + (z[9] << 72) + (z[10] << 80) + (z[11] << 88) + (z[12] << 96) + (z[13] << 104) + (z[14] << 112) + (z[15] << 120) + (z[16] << 128) + (z[17] << 136) + (z[18] << 144) + (z[19] << 152) + (z[20] << 160) + (z[21] << 168) + (z[22] << 176) + (z[23] << 184) + (z[24] << 192) + (z[25] << 200) + (z[26] << 208) + (z[27] << 216) + (z[28] << 224) + (z[29] << 232) + (z[30] << 240) + (z[31] << 248) +// +// twos_complement_eval z = let x1 := z[0] + (z[1] << 64) + (z[2] << 128) + (z[3] << 192) in +// +// if x1 & (2^256-1) < 2^255 then x1 & (2^256-1) else (x1 & (2^256-1)) - 2^256 + +package edwards25519 + +import "math/bits" + +type fiatScalarUint1 uint64 // We use uint64 instead of a more narrow type for performance reasons; see https://github.com/mit-plv/fiat-crypto/pull/1006#issuecomment-892625927 +type fiatScalarInt1 int64 // We use uint64 instead of a more narrow type for performance reasons; see https://github.com/mit-plv/fiat-crypto/pull/1006#issuecomment-892625927 + +// The type fiatScalarMontgomeryDomainFieldElement is a field element in the Montgomery domain. +// +// Bounds: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +type fiatScalarMontgomeryDomainFieldElement [4]uint64 + +// The type fiatScalarNonMontgomeryDomainFieldElement is a field element NOT in the Montgomery domain. +// +// Bounds: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +type fiatScalarNonMontgomeryDomainFieldElement [4]uint64 + +// fiatScalarCmovznzU64 is a single-word conditional move. +// +// Postconditions: +// +// out1 = (if arg1 = 0 then arg2 else arg3) +// +// Input Bounds: +// +// arg1: [0x0 ~> 0x1] +// arg2: [0x0 ~> 0xffffffffffffffff] +// arg3: [0x0 ~> 0xffffffffffffffff] +// +// Output Bounds: +// +// out1: [0x0 ~> 0xffffffffffffffff] +func fiatScalarCmovznzU64(out1 *uint64, arg1 fiatScalarUint1, arg2 uint64, arg3 uint64) { + x1 := (uint64(arg1) * 0xffffffffffffffff) + x2 := ((x1 & arg3) | ((^x1) & arg2)) + *out1 = x2 +} + +// fiatScalarMul multiplies two field elements in the Montgomery domain. +// +// Preconditions: +// +// 0 ≤ eval arg1 < m +// 0 ≤ eval arg2 < m +// +// Postconditions: +// +// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg2)) mod m +// 0 ≤ eval out1 < m +func fiatScalarMul(out1 *fiatScalarMontgomeryDomainFieldElement, arg1 *fiatScalarMontgomeryDomainFieldElement, arg2 *fiatScalarMontgomeryDomainFieldElement) { + x1 := arg1[1] + x2 := arg1[2] + x3 := arg1[3] + x4 := arg1[0] + var x5 uint64 + var x6 uint64 + x6, x5 = bits.Mul64(x4, arg2[3]) + var x7 uint64 + var x8 uint64 + x8, x7 = bits.Mul64(x4, arg2[2]) + var x9 uint64 + var x10 uint64 + x10, x9 = bits.Mul64(x4, arg2[1]) + var x11 uint64 + var x12 uint64 + x12, x11 = bits.Mul64(x4, arg2[0]) + var x13 uint64 + var x14 uint64 + x13, x14 = bits.Add64(x12, x9, uint64(0x0)) + var x15 uint64 + var x16 uint64 + x15, x16 = bits.Add64(x10, x7, uint64(fiatScalarUint1(x14))) + var x17 uint64 + var x18 uint64 + x17, x18 = bits.Add64(x8, x5, uint64(fiatScalarUint1(x16))) + x19 := (uint64(fiatScalarUint1(x18)) + x6) + var x20 uint64 + _, x20 = bits.Mul64(x11, 0xd2b51da312547e1b) + var x22 uint64 + var x23 uint64 + x23, x22 = bits.Mul64(x20, 0x1000000000000000) + var x24 uint64 + var x25 uint64 + x25, x24 = bits.Mul64(x20, 0x14def9dea2f79cd6) + var x26 uint64 + var x27 uint64 + x27, x26 = bits.Mul64(x20, 0x5812631a5cf5d3ed) + var x28 uint64 + var x29 uint64 + x28, x29 = bits.Add64(x27, x24, uint64(0x0)) + x30 := (uint64(fiatScalarUint1(x29)) + x25) + var x32 uint64 + _, x32 = bits.Add64(x11, x26, uint64(0x0)) + var x33 uint64 + var x34 uint64 + x33, x34 = bits.Add64(x13, x28, uint64(fiatScalarUint1(x32))) + var x35 uint64 + var x36 uint64 + x35, x36 = bits.Add64(x15, x30, uint64(fiatScalarUint1(x34))) + var x37 uint64 + var x38 uint64 + x37, x38 = bits.Add64(x17, x22, uint64(fiatScalarUint1(x36))) + var x39 uint64 + var x40 uint64 + x39, x40 = bits.Add64(x19, x23, uint64(fiatScalarUint1(x38))) + var x41 uint64 + var x42 uint64 + x42, x41 = bits.Mul64(x1, arg2[3]) + var x43 uint64 + var x44 uint64 + x44, x43 = bits.Mul64(x1, arg2[2]) + var x45 uint64 + var x46 uint64 + x46, x45 = bits.Mul64(x1, arg2[1]) + var x47 uint64 + var x48 uint64 + x48, x47 = bits.Mul64(x1, arg2[0]) + var x49 uint64 + var x50 uint64 + x49, x50 = bits.Add64(x48, x45, uint64(0x0)) + var x51 uint64 + var x52 uint64 + x51, x52 = bits.Add64(x46, x43, uint64(fiatScalarUint1(x50))) + var x53 uint64 + var x54 uint64 + x53, x54 = bits.Add64(x44, x41, uint64(fiatScalarUint1(x52))) + x55 := (uint64(fiatScalarUint1(x54)) + x42) + var x56 uint64 + var x57 uint64 + x56, x57 = bits.Add64(x33, x47, uint64(0x0)) + var x58 uint64 + var x59 uint64 + x58, x59 = bits.Add64(x35, x49, uint64(fiatScalarUint1(x57))) + var x60 uint64 + var x61 uint64 + x60, x61 = bits.Add64(x37, x51, uint64(fiatScalarUint1(x59))) + var x62 uint64 + var x63 uint64 + x62, x63 = bits.Add64(x39, x53, uint64(fiatScalarUint1(x61))) + var x64 uint64 + var x65 uint64 + x64, x65 = bits.Add64(uint64(fiatScalarUint1(x40)), x55, uint64(fiatScalarUint1(x63))) + var x66 uint64 + _, x66 = bits.Mul64(x56, 0xd2b51da312547e1b) + var x68 uint64 + var x69 uint64 + x69, x68 = bits.Mul64(x66, 0x1000000000000000) + var x70 uint64 + var x71 uint64 + x71, x70 = bits.Mul64(x66, 0x14def9dea2f79cd6) + var x72 uint64 + var x73 uint64 + x73, x72 = bits.Mul64(x66, 0x5812631a5cf5d3ed) + var x74 uint64 + var x75 uint64 + x74, x75 = bits.Add64(x73, x70, uint64(0x0)) + x76 := (uint64(fiatScalarUint1(x75)) + x71) + var x78 uint64 + _, x78 = bits.Add64(x56, x72, uint64(0x0)) + var x79 uint64 + var x80 uint64 + x79, x80 = bits.Add64(x58, x74, uint64(fiatScalarUint1(x78))) + var x81 uint64 + var x82 uint64 + x81, x82 = bits.Add64(x60, x76, uint64(fiatScalarUint1(x80))) + var x83 uint64 + var x84 uint64 + x83, x84 = bits.Add64(x62, x68, uint64(fiatScalarUint1(x82))) + var x85 uint64 + var x86 uint64 + x85, x86 = bits.Add64(x64, x69, uint64(fiatScalarUint1(x84))) + x87 := (uint64(fiatScalarUint1(x86)) + uint64(fiatScalarUint1(x65))) + var x88 uint64 + var x89 uint64 + x89, x88 = bits.Mul64(x2, arg2[3]) + var x90 uint64 + var x91 uint64 + x91, x90 = bits.Mul64(x2, arg2[2]) + var x92 uint64 + var x93 uint64 + x93, x92 = bits.Mul64(x2, arg2[1]) + var x94 uint64 + var x95 uint64 + x95, x94 = bits.Mul64(x2, arg2[0]) + var x96 uint64 + var x97 uint64 + x96, x97 = bits.Add64(x95, x92, uint64(0x0)) + var x98 uint64 + var x99 uint64 + x98, x99 = bits.Add64(x93, x90, uint64(fiatScalarUint1(x97))) + var x100 uint64 + var x101 uint64 + x100, x101 = bits.Add64(x91, x88, uint64(fiatScalarUint1(x99))) + x102 := (uint64(fiatScalarUint1(x101)) + x89) + var x103 uint64 + var x104 uint64 + x103, x104 = bits.Add64(x79, x94, uint64(0x0)) + var x105 uint64 + var x106 uint64 + x105, x106 = bits.Add64(x81, x96, uint64(fiatScalarUint1(x104))) + var x107 uint64 + var x108 uint64 + x107, x108 = bits.Add64(x83, x98, uint64(fiatScalarUint1(x106))) + var x109 uint64 + var x110 uint64 + x109, x110 = bits.Add64(x85, x100, uint64(fiatScalarUint1(x108))) + var x111 uint64 + var x112 uint64 + x111, x112 = bits.Add64(x87, x102, uint64(fiatScalarUint1(x110))) + var x113 uint64 + _, x113 = bits.Mul64(x103, 0xd2b51da312547e1b) + var x115 uint64 + var x116 uint64 + x116, x115 = bits.Mul64(x113, 0x1000000000000000) + var x117 uint64 + var x118 uint64 + x118, x117 = bits.Mul64(x113, 0x14def9dea2f79cd6) + var x119 uint64 + var x120 uint64 + x120, x119 = bits.Mul64(x113, 0x5812631a5cf5d3ed) + var x121 uint64 + var x122 uint64 + x121, x122 = bits.Add64(x120, x117, uint64(0x0)) + x123 := (uint64(fiatScalarUint1(x122)) + x118) + var x125 uint64 + _, x125 = bits.Add64(x103, x119, uint64(0x0)) + var x126 uint64 + var x127 uint64 + x126, x127 = bits.Add64(x105, x121, uint64(fiatScalarUint1(x125))) + var x128 uint64 + var x129 uint64 + x128, x129 = bits.Add64(x107, x123, uint64(fiatScalarUint1(x127))) + var x130 uint64 + var x131 uint64 + x130, x131 = bits.Add64(x109, x115, uint64(fiatScalarUint1(x129))) + var x132 uint64 + var x133 uint64 + x132, x133 = bits.Add64(x111, x116, uint64(fiatScalarUint1(x131))) + x134 := (uint64(fiatScalarUint1(x133)) + uint64(fiatScalarUint1(x112))) + var x135 uint64 + var x136 uint64 + x136, x135 = bits.Mul64(x3, arg2[3]) + var x137 uint64 + var x138 uint64 + x138, x137 = bits.Mul64(x3, arg2[2]) + var x139 uint64 + var x140 uint64 + x140, x139 = bits.Mul64(x3, arg2[1]) + var x141 uint64 + var x142 uint64 + x142, x141 = bits.Mul64(x3, arg2[0]) + var x143 uint64 + var x144 uint64 + x143, x144 = bits.Add64(x142, x139, uint64(0x0)) + var x145 uint64 + var x146 uint64 + x145, x146 = bits.Add64(x140, x137, uint64(fiatScalarUint1(x144))) + var x147 uint64 + var x148 uint64 + x147, x148 = bits.Add64(x138, x135, uint64(fiatScalarUint1(x146))) + x149 := (uint64(fiatScalarUint1(x148)) + x136) + var x150 uint64 + var x151 uint64 + x150, x151 = bits.Add64(x126, x141, uint64(0x0)) + var x152 uint64 + var x153 uint64 + x152, x153 = bits.Add64(x128, x143, uint64(fiatScalarUint1(x151))) + var x154 uint64 + var x155 uint64 + x154, x155 = bits.Add64(x130, x145, uint64(fiatScalarUint1(x153))) + var x156 uint64 + var x157 uint64 + x156, x157 = bits.Add64(x132, x147, uint64(fiatScalarUint1(x155))) + var x158 uint64 + var x159 uint64 + x158, x159 = bits.Add64(x134, x149, uint64(fiatScalarUint1(x157))) + var x160 uint64 + _, x160 = bits.Mul64(x150, 0xd2b51da312547e1b) + var x162 uint64 + var x163 uint64 + x163, x162 = bits.Mul64(x160, 0x1000000000000000) + var x164 uint64 + var x165 uint64 + x165, x164 = bits.Mul64(x160, 0x14def9dea2f79cd6) + var x166 uint64 + var x167 uint64 + x167, x166 = bits.Mul64(x160, 0x5812631a5cf5d3ed) + var x168 uint64 + var x169 uint64 + x168, x169 = bits.Add64(x167, x164, uint64(0x0)) + x170 := (uint64(fiatScalarUint1(x169)) + x165) + var x172 uint64 + _, x172 = bits.Add64(x150, x166, uint64(0x0)) + var x173 uint64 + var x174 uint64 + x173, x174 = bits.Add64(x152, x168, uint64(fiatScalarUint1(x172))) + var x175 uint64 + var x176 uint64 + x175, x176 = bits.Add64(x154, x170, uint64(fiatScalarUint1(x174))) + var x177 uint64 + var x178 uint64 + x177, x178 = bits.Add64(x156, x162, uint64(fiatScalarUint1(x176))) + var x179 uint64 + var x180 uint64 + x179, x180 = bits.Add64(x158, x163, uint64(fiatScalarUint1(x178))) + x181 := (uint64(fiatScalarUint1(x180)) + uint64(fiatScalarUint1(x159))) + var x182 uint64 + var x183 uint64 + x182, x183 = bits.Sub64(x173, 0x5812631a5cf5d3ed, uint64(0x0)) + var x184 uint64 + var x185 uint64 + x184, x185 = bits.Sub64(x175, 0x14def9dea2f79cd6, uint64(fiatScalarUint1(x183))) + var x186 uint64 + var x187 uint64 + x186, x187 = bits.Sub64(x177, uint64(0x0), uint64(fiatScalarUint1(x185))) + var x188 uint64 + var x189 uint64 + x188, x189 = bits.Sub64(x179, 0x1000000000000000, uint64(fiatScalarUint1(x187))) + var x191 uint64 + _, x191 = bits.Sub64(x181, uint64(0x0), uint64(fiatScalarUint1(x189))) + var x192 uint64 + fiatScalarCmovznzU64(&x192, fiatScalarUint1(x191), x182, x173) + var x193 uint64 + fiatScalarCmovznzU64(&x193, fiatScalarUint1(x191), x184, x175) + var x194 uint64 + fiatScalarCmovznzU64(&x194, fiatScalarUint1(x191), x186, x177) + var x195 uint64 + fiatScalarCmovznzU64(&x195, fiatScalarUint1(x191), x188, x179) + out1[0] = x192 + out1[1] = x193 + out1[2] = x194 + out1[3] = x195 +} + +// fiatScalarAdd adds two field elements in the Montgomery domain. +// +// Preconditions: +// +// 0 ≤ eval arg1 < m +// 0 ≤ eval arg2 < m +// +// Postconditions: +// +// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) + eval (from_montgomery arg2)) mod m +// 0 ≤ eval out1 < m +func fiatScalarAdd(out1 *fiatScalarMontgomeryDomainFieldElement, arg1 *fiatScalarMontgomeryDomainFieldElement, arg2 *fiatScalarMontgomeryDomainFieldElement) { + var x1 uint64 + var x2 uint64 + x1, x2 = bits.Add64(arg1[0], arg2[0], uint64(0x0)) + var x3 uint64 + var x4 uint64 + x3, x4 = bits.Add64(arg1[1], arg2[1], uint64(fiatScalarUint1(x2))) + var x5 uint64 + var x6 uint64 + x5, x6 = bits.Add64(arg1[2], arg2[2], uint64(fiatScalarUint1(x4))) + var x7 uint64 + var x8 uint64 + x7, x8 = bits.Add64(arg1[3], arg2[3], uint64(fiatScalarUint1(x6))) + var x9 uint64 + var x10 uint64 + x9, x10 = bits.Sub64(x1, 0x5812631a5cf5d3ed, uint64(0x0)) + var x11 uint64 + var x12 uint64 + x11, x12 = bits.Sub64(x3, 0x14def9dea2f79cd6, uint64(fiatScalarUint1(x10))) + var x13 uint64 + var x14 uint64 + x13, x14 = bits.Sub64(x5, uint64(0x0), uint64(fiatScalarUint1(x12))) + var x15 uint64 + var x16 uint64 + x15, x16 = bits.Sub64(x7, 0x1000000000000000, uint64(fiatScalarUint1(x14))) + var x18 uint64 + _, x18 = bits.Sub64(uint64(fiatScalarUint1(x8)), uint64(0x0), uint64(fiatScalarUint1(x16))) + var x19 uint64 + fiatScalarCmovznzU64(&x19, fiatScalarUint1(x18), x9, x1) + var x20 uint64 + fiatScalarCmovznzU64(&x20, fiatScalarUint1(x18), x11, x3) + var x21 uint64 + fiatScalarCmovznzU64(&x21, fiatScalarUint1(x18), x13, x5) + var x22 uint64 + fiatScalarCmovznzU64(&x22, fiatScalarUint1(x18), x15, x7) + out1[0] = x19 + out1[1] = x20 + out1[2] = x21 + out1[3] = x22 +} + +// fiatScalarSub subtracts two field elements in the Montgomery domain. +// +// Preconditions: +// +// 0 ≤ eval arg1 < m +// 0 ≤ eval arg2 < m +// +// Postconditions: +// +// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) - eval (from_montgomery arg2)) mod m +// 0 ≤ eval out1 < m +func fiatScalarSub(out1 *fiatScalarMontgomeryDomainFieldElement, arg1 *fiatScalarMontgomeryDomainFieldElement, arg2 *fiatScalarMontgomeryDomainFieldElement) { + var x1 uint64 + var x2 uint64 + x1, x2 = bits.Sub64(arg1[0], arg2[0], uint64(0x0)) + var x3 uint64 + var x4 uint64 + x3, x4 = bits.Sub64(arg1[1], arg2[1], uint64(fiatScalarUint1(x2))) + var x5 uint64 + var x6 uint64 + x5, x6 = bits.Sub64(arg1[2], arg2[2], uint64(fiatScalarUint1(x4))) + var x7 uint64 + var x8 uint64 + x7, x8 = bits.Sub64(arg1[3], arg2[3], uint64(fiatScalarUint1(x6))) + var x9 uint64 + fiatScalarCmovznzU64(&x9, fiatScalarUint1(x8), uint64(0x0), 0xffffffffffffffff) + var x10 uint64 + var x11 uint64 + x10, x11 = bits.Add64(x1, (x9 & 0x5812631a5cf5d3ed), uint64(0x0)) + var x12 uint64 + var x13 uint64 + x12, x13 = bits.Add64(x3, (x9 & 0x14def9dea2f79cd6), uint64(fiatScalarUint1(x11))) + var x14 uint64 + var x15 uint64 + x14, x15 = bits.Add64(x5, uint64(0x0), uint64(fiatScalarUint1(x13))) + var x16 uint64 + x16, _ = bits.Add64(x7, (x9 & 0x1000000000000000), uint64(fiatScalarUint1(x15))) + out1[0] = x10 + out1[1] = x12 + out1[2] = x14 + out1[3] = x16 +} + +// fiatScalarOpp negates a field element in the Montgomery domain. +// +// Preconditions: +// +// 0 ≤ eval arg1 < m +// +// Postconditions: +// +// eval (from_montgomery out1) mod m = -eval (from_montgomery arg1) mod m +// 0 ≤ eval out1 < m +func fiatScalarOpp(out1 *fiatScalarMontgomeryDomainFieldElement, arg1 *fiatScalarMontgomeryDomainFieldElement) { + var x1 uint64 + var x2 uint64 + x1, x2 = bits.Sub64(uint64(0x0), arg1[0], uint64(0x0)) + var x3 uint64 + var x4 uint64 + x3, x4 = bits.Sub64(uint64(0x0), arg1[1], uint64(fiatScalarUint1(x2))) + var x5 uint64 + var x6 uint64 + x5, x6 = bits.Sub64(uint64(0x0), arg1[2], uint64(fiatScalarUint1(x4))) + var x7 uint64 + var x8 uint64 + x7, x8 = bits.Sub64(uint64(0x0), arg1[3], uint64(fiatScalarUint1(x6))) + var x9 uint64 + fiatScalarCmovznzU64(&x9, fiatScalarUint1(x8), uint64(0x0), 0xffffffffffffffff) + var x10 uint64 + var x11 uint64 + x10, x11 = bits.Add64(x1, (x9 & 0x5812631a5cf5d3ed), uint64(0x0)) + var x12 uint64 + var x13 uint64 + x12, x13 = bits.Add64(x3, (x9 & 0x14def9dea2f79cd6), uint64(fiatScalarUint1(x11))) + var x14 uint64 + var x15 uint64 + x14, x15 = bits.Add64(x5, uint64(0x0), uint64(fiatScalarUint1(x13))) + var x16 uint64 + x16, _ = bits.Add64(x7, (x9 & 0x1000000000000000), uint64(fiatScalarUint1(x15))) + out1[0] = x10 + out1[1] = x12 + out1[2] = x14 + out1[3] = x16 +} + +// fiatScalarNonzero outputs a single non-zero word if the input is non-zero and zero otherwise. +// +// Preconditions: +// +// 0 ≤ eval arg1 < m +// +// Postconditions: +// +// out1 = 0 ↔ eval (from_montgomery arg1) mod m = 0 +// +// Input Bounds: +// +// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +// +// Output Bounds: +// +// out1: [0x0 ~> 0xffffffffffffffff] +func fiatScalarNonzero(out1 *uint64, arg1 *[4]uint64) { + x1 := (arg1[0] | (arg1[1] | (arg1[2] | arg1[3]))) + *out1 = x1 +} + +// fiatScalarFromMontgomery translates a field element out of the Montgomery domain. +// +// Preconditions: +// +// 0 ≤ eval arg1 < m +// +// Postconditions: +// +// eval out1 mod m = (eval arg1 * ((2^64)⁻¹ mod m)^4) mod m +// 0 ≤ eval out1 < m +func fiatScalarFromMontgomery(out1 *fiatScalarNonMontgomeryDomainFieldElement, arg1 *fiatScalarMontgomeryDomainFieldElement) { + x1 := arg1[0] + var x2 uint64 + _, x2 = bits.Mul64(x1, 0xd2b51da312547e1b) + var x4 uint64 + var x5 uint64 + x5, x4 = bits.Mul64(x2, 0x1000000000000000) + var x6 uint64 + var x7 uint64 + x7, x6 = bits.Mul64(x2, 0x14def9dea2f79cd6) + var x8 uint64 + var x9 uint64 + x9, x8 = bits.Mul64(x2, 0x5812631a5cf5d3ed) + var x10 uint64 + var x11 uint64 + x10, x11 = bits.Add64(x9, x6, uint64(0x0)) + var x13 uint64 + _, x13 = bits.Add64(x1, x8, uint64(0x0)) + var x14 uint64 + var x15 uint64 + x14, x15 = bits.Add64(uint64(0x0), x10, uint64(fiatScalarUint1(x13))) + var x16 uint64 + var x17 uint64 + x16, x17 = bits.Add64(x14, arg1[1], uint64(0x0)) + var x18 uint64 + _, x18 = bits.Mul64(x16, 0xd2b51da312547e1b) + var x20 uint64 + var x21 uint64 + x21, x20 = bits.Mul64(x18, 0x1000000000000000) + var x22 uint64 + var x23 uint64 + x23, x22 = bits.Mul64(x18, 0x14def9dea2f79cd6) + var x24 uint64 + var x25 uint64 + x25, x24 = bits.Mul64(x18, 0x5812631a5cf5d3ed) + var x26 uint64 + var x27 uint64 + x26, x27 = bits.Add64(x25, x22, uint64(0x0)) + var x29 uint64 + _, x29 = bits.Add64(x16, x24, uint64(0x0)) + var x30 uint64 + var x31 uint64 + x30, x31 = bits.Add64((uint64(fiatScalarUint1(x17)) + (uint64(fiatScalarUint1(x15)) + (uint64(fiatScalarUint1(x11)) + x7))), x26, uint64(fiatScalarUint1(x29))) + var x32 uint64 + var x33 uint64 + x32, x33 = bits.Add64(x4, (uint64(fiatScalarUint1(x27)) + x23), uint64(fiatScalarUint1(x31))) + var x34 uint64 + var x35 uint64 + x34, x35 = bits.Add64(x5, x20, uint64(fiatScalarUint1(x33))) + var x36 uint64 + var x37 uint64 + x36, x37 = bits.Add64(x30, arg1[2], uint64(0x0)) + var x38 uint64 + var x39 uint64 + x38, x39 = bits.Add64(x32, uint64(0x0), uint64(fiatScalarUint1(x37))) + var x40 uint64 + var x41 uint64 + x40, x41 = bits.Add64(x34, uint64(0x0), uint64(fiatScalarUint1(x39))) + var x42 uint64 + _, x42 = bits.Mul64(x36, 0xd2b51da312547e1b) + var x44 uint64 + var x45 uint64 + x45, x44 = bits.Mul64(x42, 0x1000000000000000) + var x46 uint64 + var x47 uint64 + x47, x46 = bits.Mul64(x42, 0x14def9dea2f79cd6) + var x48 uint64 + var x49 uint64 + x49, x48 = bits.Mul64(x42, 0x5812631a5cf5d3ed) + var x50 uint64 + var x51 uint64 + x50, x51 = bits.Add64(x49, x46, uint64(0x0)) + var x53 uint64 + _, x53 = bits.Add64(x36, x48, uint64(0x0)) + var x54 uint64 + var x55 uint64 + x54, x55 = bits.Add64(x38, x50, uint64(fiatScalarUint1(x53))) + var x56 uint64 + var x57 uint64 + x56, x57 = bits.Add64(x40, (uint64(fiatScalarUint1(x51)) + x47), uint64(fiatScalarUint1(x55))) + var x58 uint64 + var x59 uint64 + x58, x59 = bits.Add64((uint64(fiatScalarUint1(x41)) + (uint64(fiatScalarUint1(x35)) + x21)), x44, uint64(fiatScalarUint1(x57))) + var x60 uint64 + var x61 uint64 + x60, x61 = bits.Add64(x54, arg1[3], uint64(0x0)) + var x62 uint64 + var x63 uint64 + x62, x63 = bits.Add64(x56, uint64(0x0), uint64(fiatScalarUint1(x61))) + var x64 uint64 + var x65 uint64 + x64, x65 = bits.Add64(x58, uint64(0x0), uint64(fiatScalarUint1(x63))) + var x66 uint64 + _, x66 = bits.Mul64(x60, 0xd2b51da312547e1b) + var x68 uint64 + var x69 uint64 + x69, x68 = bits.Mul64(x66, 0x1000000000000000) + var x70 uint64 + var x71 uint64 + x71, x70 = bits.Mul64(x66, 0x14def9dea2f79cd6) + var x72 uint64 + var x73 uint64 + x73, x72 = bits.Mul64(x66, 0x5812631a5cf5d3ed) + var x74 uint64 + var x75 uint64 + x74, x75 = bits.Add64(x73, x70, uint64(0x0)) + var x77 uint64 + _, x77 = bits.Add64(x60, x72, uint64(0x0)) + var x78 uint64 + var x79 uint64 + x78, x79 = bits.Add64(x62, x74, uint64(fiatScalarUint1(x77))) + var x80 uint64 + var x81 uint64 + x80, x81 = bits.Add64(x64, (uint64(fiatScalarUint1(x75)) + x71), uint64(fiatScalarUint1(x79))) + var x82 uint64 + var x83 uint64 + x82, x83 = bits.Add64((uint64(fiatScalarUint1(x65)) + (uint64(fiatScalarUint1(x59)) + x45)), x68, uint64(fiatScalarUint1(x81))) + x84 := (uint64(fiatScalarUint1(x83)) + x69) + var x85 uint64 + var x86 uint64 + x85, x86 = bits.Sub64(x78, 0x5812631a5cf5d3ed, uint64(0x0)) + var x87 uint64 + var x88 uint64 + x87, x88 = bits.Sub64(x80, 0x14def9dea2f79cd6, uint64(fiatScalarUint1(x86))) + var x89 uint64 + var x90 uint64 + x89, x90 = bits.Sub64(x82, uint64(0x0), uint64(fiatScalarUint1(x88))) + var x91 uint64 + var x92 uint64 + x91, x92 = bits.Sub64(x84, 0x1000000000000000, uint64(fiatScalarUint1(x90))) + var x94 uint64 + _, x94 = bits.Sub64(uint64(0x0), uint64(0x0), uint64(fiatScalarUint1(x92))) + var x95 uint64 + fiatScalarCmovznzU64(&x95, fiatScalarUint1(x94), x85, x78) + var x96 uint64 + fiatScalarCmovznzU64(&x96, fiatScalarUint1(x94), x87, x80) + var x97 uint64 + fiatScalarCmovznzU64(&x97, fiatScalarUint1(x94), x89, x82) + var x98 uint64 + fiatScalarCmovznzU64(&x98, fiatScalarUint1(x94), x91, x84) + out1[0] = x95 + out1[1] = x96 + out1[2] = x97 + out1[3] = x98 +} + +// fiatScalarToMontgomery translates a field element into the Montgomery domain. +// +// Preconditions: +// +// 0 ≤ eval arg1 < m +// +// Postconditions: +// +// eval (from_montgomery out1) mod m = eval arg1 mod m +// 0 ≤ eval out1 < m +func fiatScalarToMontgomery(out1 *fiatScalarMontgomeryDomainFieldElement, arg1 *fiatScalarNonMontgomeryDomainFieldElement) { + x1 := arg1[1] + x2 := arg1[2] + x3 := arg1[3] + x4 := arg1[0] + var x5 uint64 + var x6 uint64 + x6, x5 = bits.Mul64(x4, 0x399411b7c309a3d) + var x7 uint64 + var x8 uint64 + x8, x7 = bits.Mul64(x4, 0xceec73d217f5be65) + var x9 uint64 + var x10 uint64 + x10, x9 = bits.Mul64(x4, 0xd00e1ba768859347) + var x11 uint64 + var x12 uint64 + x12, x11 = bits.Mul64(x4, 0xa40611e3449c0f01) + var x13 uint64 + var x14 uint64 + x13, x14 = bits.Add64(x12, x9, uint64(0x0)) + var x15 uint64 + var x16 uint64 + x15, x16 = bits.Add64(x10, x7, uint64(fiatScalarUint1(x14))) + var x17 uint64 + var x18 uint64 + x17, x18 = bits.Add64(x8, x5, uint64(fiatScalarUint1(x16))) + var x19 uint64 + _, x19 = bits.Mul64(x11, 0xd2b51da312547e1b) + var x21 uint64 + var x22 uint64 + x22, x21 = bits.Mul64(x19, 0x1000000000000000) + var x23 uint64 + var x24 uint64 + x24, x23 = bits.Mul64(x19, 0x14def9dea2f79cd6) + var x25 uint64 + var x26 uint64 + x26, x25 = bits.Mul64(x19, 0x5812631a5cf5d3ed) + var x27 uint64 + var x28 uint64 + x27, x28 = bits.Add64(x26, x23, uint64(0x0)) + var x30 uint64 + _, x30 = bits.Add64(x11, x25, uint64(0x0)) + var x31 uint64 + var x32 uint64 + x31, x32 = bits.Add64(x13, x27, uint64(fiatScalarUint1(x30))) + var x33 uint64 + var x34 uint64 + x33, x34 = bits.Add64(x15, (uint64(fiatScalarUint1(x28)) + x24), uint64(fiatScalarUint1(x32))) + var x35 uint64 + var x36 uint64 + x35, x36 = bits.Add64(x17, x21, uint64(fiatScalarUint1(x34))) + var x37 uint64 + var x38 uint64 + x38, x37 = bits.Mul64(x1, 0x399411b7c309a3d) + var x39 uint64 + var x40 uint64 + x40, x39 = bits.Mul64(x1, 0xceec73d217f5be65) + var x41 uint64 + var x42 uint64 + x42, x41 = bits.Mul64(x1, 0xd00e1ba768859347) + var x43 uint64 + var x44 uint64 + x44, x43 = bits.Mul64(x1, 0xa40611e3449c0f01) + var x45 uint64 + var x46 uint64 + x45, x46 = bits.Add64(x44, x41, uint64(0x0)) + var x47 uint64 + var x48 uint64 + x47, x48 = bits.Add64(x42, x39, uint64(fiatScalarUint1(x46))) + var x49 uint64 + var x50 uint64 + x49, x50 = bits.Add64(x40, x37, uint64(fiatScalarUint1(x48))) + var x51 uint64 + var x52 uint64 + x51, x52 = bits.Add64(x31, x43, uint64(0x0)) + var x53 uint64 + var x54 uint64 + x53, x54 = bits.Add64(x33, x45, uint64(fiatScalarUint1(x52))) + var x55 uint64 + var x56 uint64 + x55, x56 = bits.Add64(x35, x47, uint64(fiatScalarUint1(x54))) + var x57 uint64 + var x58 uint64 + x57, x58 = bits.Add64(((uint64(fiatScalarUint1(x36)) + (uint64(fiatScalarUint1(x18)) + x6)) + x22), x49, uint64(fiatScalarUint1(x56))) + var x59 uint64 + _, x59 = bits.Mul64(x51, 0xd2b51da312547e1b) + var x61 uint64 + var x62 uint64 + x62, x61 = bits.Mul64(x59, 0x1000000000000000) + var x63 uint64 + var x64 uint64 + x64, x63 = bits.Mul64(x59, 0x14def9dea2f79cd6) + var x65 uint64 + var x66 uint64 + x66, x65 = bits.Mul64(x59, 0x5812631a5cf5d3ed) + var x67 uint64 + var x68 uint64 + x67, x68 = bits.Add64(x66, x63, uint64(0x0)) + var x70 uint64 + _, x70 = bits.Add64(x51, x65, uint64(0x0)) + var x71 uint64 + var x72 uint64 + x71, x72 = bits.Add64(x53, x67, uint64(fiatScalarUint1(x70))) + var x73 uint64 + var x74 uint64 + x73, x74 = bits.Add64(x55, (uint64(fiatScalarUint1(x68)) + x64), uint64(fiatScalarUint1(x72))) + var x75 uint64 + var x76 uint64 + x75, x76 = bits.Add64(x57, x61, uint64(fiatScalarUint1(x74))) + var x77 uint64 + var x78 uint64 + x78, x77 = bits.Mul64(x2, 0x399411b7c309a3d) + var x79 uint64 + var x80 uint64 + x80, x79 = bits.Mul64(x2, 0xceec73d217f5be65) + var x81 uint64 + var x82 uint64 + x82, x81 = bits.Mul64(x2, 0xd00e1ba768859347) + var x83 uint64 + var x84 uint64 + x84, x83 = bits.Mul64(x2, 0xa40611e3449c0f01) + var x85 uint64 + var x86 uint64 + x85, x86 = bits.Add64(x84, x81, uint64(0x0)) + var x87 uint64 + var x88 uint64 + x87, x88 = bits.Add64(x82, x79, uint64(fiatScalarUint1(x86))) + var x89 uint64 + var x90 uint64 + x89, x90 = bits.Add64(x80, x77, uint64(fiatScalarUint1(x88))) + var x91 uint64 + var x92 uint64 + x91, x92 = bits.Add64(x71, x83, uint64(0x0)) + var x93 uint64 + var x94 uint64 + x93, x94 = bits.Add64(x73, x85, uint64(fiatScalarUint1(x92))) + var x95 uint64 + var x96 uint64 + x95, x96 = bits.Add64(x75, x87, uint64(fiatScalarUint1(x94))) + var x97 uint64 + var x98 uint64 + x97, x98 = bits.Add64(((uint64(fiatScalarUint1(x76)) + (uint64(fiatScalarUint1(x58)) + (uint64(fiatScalarUint1(x50)) + x38))) + x62), x89, uint64(fiatScalarUint1(x96))) + var x99 uint64 + _, x99 = bits.Mul64(x91, 0xd2b51da312547e1b) + var x101 uint64 + var x102 uint64 + x102, x101 = bits.Mul64(x99, 0x1000000000000000) + var x103 uint64 + var x104 uint64 + x104, x103 = bits.Mul64(x99, 0x14def9dea2f79cd6) + var x105 uint64 + var x106 uint64 + x106, x105 = bits.Mul64(x99, 0x5812631a5cf5d3ed) + var x107 uint64 + var x108 uint64 + x107, x108 = bits.Add64(x106, x103, uint64(0x0)) + var x110 uint64 + _, x110 = bits.Add64(x91, x105, uint64(0x0)) + var x111 uint64 + var x112 uint64 + x111, x112 = bits.Add64(x93, x107, uint64(fiatScalarUint1(x110))) + var x113 uint64 + var x114 uint64 + x113, x114 = bits.Add64(x95, (uint64(fiatScalarUint1(x108)) + x104), uint64(fiatScalarUint1(x112))) + var x115 uint64 + var x116 uint64 + x115, x116 = bits.Add64(x97, x101, uint64(fiatScalarUint1(x114))) + var x117 uint64 + var x118 uint64 + x118, x117 = bits.Mul64(x3, 0x399411b7c309a3d) + var x119 uint64 + var x120 uint64 + x120, x119 = bits.Mul64(x3, 0xceec73d217f5be65) + var x121 uint64 + var x122 uint64 + x122, x121 = bits.Mul64(x3, 0xd00e1ba768859347) + var x123 uint64 + var x124 uint64 + x124, x123 = bits.Mul64(x3, 0xa40611e3449c0f01) + var x125 uint64 + var x126 uint64 + x125, x126 = bits.Add64(x124, x121, uint64(0x0)) + var x127 uint64 + var x128 uint64 + x127, x128 = bits.Add64(x122, x119, uint64(fiatScalarUint1(x126))) + var x129 uint64 + var x130 uint64 + x129, x130 = bits.Add64(x120, x117, uint64(fiatScalarUint1(x128))) + var x131 uint64 + var x132 uint64 + x131, x132 = bits.Add64(x111, x123, uint64(0x0)) + var x133 uint64 + var x134 uint64 + x133, x134 = bits.Add64(x113, x125, uint64(fiatScalarUint1(x132))) + var x135 uint64 + var x136 uint64 + x135, x136 = bits.Add64(x115, x127, uint64(fiatScalarUint1(x134))) + var x137 uint64 + var x138 uint64 + x137, x138 = bits.Add64(((uint64(fiatScalarUint1(x116)) + (uint64(fiatScalarUint1(x98)) + (uint64(fiatScalarUint1(x90)) + x78))) + x102), x129, uint64(fiatScalarUint1(x136))) + var x139 uint64 + _, x139 = bits.Mul64(x131, 0xd2b51da312547e1b) + var x141 uint64 + var x142 uint64 + x142, x141 = bits.Mul64(x139, 0x1000000000000000) + var x143 uint64 + var x144 uint64 + x144, x143 = bits.Mul64(x139, 0x14def9dea2f79cd6) + var x145 uint64 + var x146 uint64 + x146, x145 = bits.Mul64(x139, 0x5812631a5cf5d3ed) + var x147 uint64 + var x148 uint64 + x147, x148 = bits.Add64(x146, x143, uint64(0x0)) + var x150 uint64 + _, x150 = bits.Add64(x131, x145, uint64(0x0)) + var x151 uint64 + var x152 uint64 + x151, x152 = bits.Add64(x133, x147, uint64(fiatScalarUint1(x150))) + var x153 uint64 + var x154 uint64 + x153, x154 = bits.Add64(x135, (uint64(fiatScalarUint1(x148)) + x144), uint64(fiatScalarUint1(x152))) + var x155 uint64 + var x156 uint64 + x155, x156 = bits.Add64(x137, x141, uint64(fiatScalarUint1(x154))) + x157 := ((uint64(fiatScalarUint1(x156)) + (uint64(fiatScalarUint1(x138)) + (uint64(fiatScalarUint1(x130)) + x118))) + x142) + var x158 uint64 + var x159 uint64 + x158, x159 = bits.Sub64(x151, 0x5812631a5cf5d3ed, uint64(0x0)) + var x160 uint64 + var x161 uint64 + x160, x161 = bits.Sub64(x153, 0x14def9dea2f79cd6, uint64(fiatScalarUint1(x159))) + var x162 uint64 + var x163 uint64 + x162, x163 = bits.Sub64(x155, uint64(0x0), uint64(fiatScalarUint1(x161))) + var x164 uint64 + var x165 uint64 + x164, x165 = bits.Sub64(x157, 0x1000000000000000, uint64(fiatScalarUint1(x163))) + var x167 uint64 + _, x167 = bits.Sub64(uint64(0x0), uint64(0x0), uint64(fiatScalarUint1(x165))) + var x168 uint64 + fiatScalarCmovznzU64(&x168, fiatScalarUint1(x167), x158, x151) + var x169 uint64 + fiatScalarCmovznzU64(&x169, fiatScalarUint1(x167), x160, x153) + var x170 uint64 + fiatScalarCmovznzU64(&x170, fiatScalarUint1(x167), x162, x155) + var x171 uint64 + fiatScalarCmovznzU64(&x171, fiatScalarUint1(x167), x164, x157) + out1[0] = x168 + out1[1] = x169 + out1[2] = x170 + out1[3] = x171 +} + +// fiatScalarToBytes serializes a field element NOT in the Montgomery domain to bytes in little-endian order. +// +// Preconditions: +// +// 0 ≤ eval arg1 < m +// +// Postconditions: +// +// out1 = map (λ x, ⌊((eval arg1 mod m) mod 2^(8 * (x + 1))) / 2^(8 * x)⌋) [0..31] +// +// Input Bounds: +// +// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0x1fffffffffffffff]] +// +// Output Bounds: +// +// out1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0x1f]] +func fiatScalarToBytes(out1 *[32]uint8, arg1 *[4]uint64) { + x1 := arg1[3] + x2 := arg1[2] + x3 := arg1[1] + x4 := arg1[0] + x5 := (uint8(x4) & 0xff) + x6 := (x4 >> 8) + x7 := (uint8(x6) & 0xff) + x8 := (x6 >> 8) + x9 := (uint8(x8) & 0xff) + x10 := (x8 >> 8) + x11 := (uint8(x10) & 0xff) + x12 := (x10 >> 8) + x13 := (uint8(x12) & 0xff) + x14 := (x12 >> 8) + x15 := (uint8(x14) & 0xff) + x16 := (x14 >> 8) + x17 := (uint8(x16) & 0xff) + x18 := uint8((x16 >> 8)) + x19 := (uint8(x3) & 0xff) + x20 := (x3 >> 8) + x21 := (uint8(x20) & 0xff) + x22 := (x20 >> 8) + x23 := (uint8(x22) & 0xff) + x24 := (x22 >> 8) + x25 := (uint8(x24) & 0xff) + x26 := (x24 >> 8) + x27 := (uint8(x26) & 0xff) + x28 := (x26 >> 8) + x29 := (uint8(x28) & 0xff) + x30 := (x28 >> 8) + x31 := (uint8(x30) & 0xff) + x32 := uint8((x30 >> 8)) + x33 := (uint8(x2) & 0xff) + x34 := (x2 >> 8) + x35 := (uint8(x34) & 0xff) + x36 := (x34 >> 8) + x37 := (uint8(x36) & 0xff) + x38 := (x36 >> 8) + x39 := (uint8(x38) & 0xff) + x40 := (x38 >> 8) + x41 := (uint8(x40) & 0xff) + x42 := (x40 >> 8) + x43 := (uint8(x42) & 0xff) + x44 := (x42 >> 8) + x45 := (uint8(x44) & 0xff) + x46 := uint8((x44 >> 8)) + x47 := (uint8(x1) & 0xff) + x48 := (x1 >> 8) + x49 := (uint8(x48) & 0xff) + x50 := (x48 >> 8) + x51 := (uint8(x50) & 0xff) + x52 := (x50 >> 8) + x53 := (uint8(x52) & 0xff) + x54 := (x52 >> 8) + x55 := (uint8(x54) & 0xff) + x56 := (x54 >> 8) + x57 := (uint8(x56) & 0xff) + x58 := (x56 >> 8) + x59 := (uint8(x58) & 0xff) + x60 := uint8((x58 >> 8)) + out1[0] = x5 + out1[1] = x7 + out1[2] = x9 + out1[3] = x11 + out1[4] = x13 + out1[5] = x15 + out1[6] = x17 + out1[7] = x18 + out1[8] = x19 + out1[9] = x21 + out1[10] = x23 + out1[11] = x25 + out1[12] = x27 + out1[13] = x29 + out1[14] = x31 + out1[15] = x32 + out1[16] = x33 + out1[17] = x35 + out1[18] = x37 + out1[19] = x39 + out1[20] = x41 + out1[21] = x43 + out1[22] = x45 + out1[23] = x46 + out1[24] = x47 + out1[25] = x49 + out1[26] = x51 + out1[27] = x53 + out1[28] = x55 + out1[29] = x57 + out1[30] = x59 + out1[31] = x60 +} + +// fiatScalarFromBytes deserializes a field element NOT in the Montgomery domain from bytes in little-endian order. +// +// Preconditions: +// +// 0 ≤ bytes_eval arg1 < m +// +// Postconditions: +// +// eval out1 mod m = bytes_eval arg1 mod m +// 0 ≤ eval out1 < m +// +// Input Bounds: +// +// arg1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0x1f]] +// +// Output Bounds: +// +// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0x1fffffffffffffff]] +func fiatScalarFromBytes(out1 *[4]uint64, arg1 *[32]uint8) { + x1 := (uint64(arg1[31]) << 56) + x2 := (uint64(arg1[30]) << 48) + x3 := (uint64(arg1[29]) << 40) + x4 := (uint64(arg1[28]) << 32) + x5 := (uint64(arg1[27]) << 24) + x6 := (uint64(arg1[26]) << 16) + x7 := (uint64(arg1[25]) << 8) + x8 := arg1[24] + x9 := (uint64(arg1[23]) << 56) + x10 := (uint64(arg1[22]) << 48) + x11 := (uint64(arg1[21]) << 40) + x12 := (uint64(arg1[20]) << 32) + x13 := (uint64(arg1[19]) << 24) + x14 := (uint64(arg1[18]) << 16) + x15 := (uint64(arg1[17]) << 8) + x16 := arg1[16] + x17 := (uint64(arg1[15]) << 56) + x18 := (uint64(arg1[14]) << 48) + x19 := (uint64(arg1[13]) << 40) + x20 := (uint64(arg1[12]) << 32) + x21 := (uint64(arg1[11]) << 24) + x22 := (uint64(arg1[10]) << 16) + x23 := (uint64(arg1[9]) << 8) + x24 := arg1[8] + x25 := (uint64(arg1[7]) << 56) + x26 := (uint64(arg1[6]) << 48) + x27 := (uint64(arg1[5]) << 40) + x28 := (uint64(arg1[4]) << 32) + x29 := (uint64(arg1[3]) << 24) + x30 := (uint64(arg1[2]) << 16) + x31 := (uint64(arg1[1]) << 8) + x32 := arg1[0] + x33 := (x31 + uint64(x32)) + x34 := (x30 + x33) + x35 := (x29 + x34) + x36 := (x28 + x35) + x37 := (x27 + x36) + x38 := (x26 + x37) + x39 := (x25 + x38) + x40 := (x23 + uint64(x24)) + x41 := (x22 + x40) + x42 := (x21 + x41) + x43 := (x20 + x42) + x44 := (x19 + x43) + x45 := (x18 + x44) + x46 := (x17 + x45) + x47 := (x15 + uint64(x16)) + x48 := (x14 + x47) + x49 := (x13 + x48) + x50 := (x12 + x49) + x51 := (x11 + x50) + x52 := (x10 + x51) + x53 := (x9 + x52) + x54 := (x7 + uint64(x8)) + x55 := (x6 + x54) + x56 := (x5 + x55) + x57 := (x4 + x56) + x58 := (x3 + x57) + x59 := (x2 + x58) + x60 := (x1 + x59) + out1[0] = x39 + out1[1] = x46 + out1[2] = x53 + out1[3] = x60 +} diff --git a/vendor/filippo.io/edwards25519/scalarmult.go b/vendor/filippo.io/edwards25519/scalarmult.go new file mode 100644 index 00000000..f7ca3cef --- /dev/null +++ b/vendor/filippo.io/edwards25519/scalarmult.go @@ -0,0 +1,214 @@ +// Copyright (c) 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package edwards25519 + +import "sync" + +// basepointTable is a set of 32 affineLookupTables, where table i is generated +// from 256i * basepoint. It is precomputed the first time it's used. +func basepointTable() *[32]affineLookupTable { + basepointTablePrecomp.initOnce.Do(func() { + p := NewGeneratorPoint() + for i := 0; i < 32; i++ { + basepointTablePrecomp.table[i].FromP3(p) + for j := 0; j < 8; j++ { + p.Add(p, p) + } + } + }) + return &basepointTablePrecomp.table +} + +var basepointTablePrecomp struct { + table [32]affineLookupTable + initOnce sync.Once +} + +// ScalarBaseMult sets v = x * B, where B is the canonical generator, and +// returns v. +// +// The scalar multiplication is done in constant time. +func (v *Point) ScalarBaseMult(x *Scalar) *Point { + basepointTable := basepointTable() + + // Write x = sum(x_i * 16^i) so x*B = sum( B*x_i*16^i ) + // as described in the Ed25519 paper + // + // Group even and odd coefficients + // x*B = x_0*16^0*B + x_2*16^2*B + ... + x_62*16^62*B + // + x_1*16^1*B + x_3*16^3*B + ... + x_63*16^63*B + // x*B = x_0*16^0*B + x_2*16^2*B + ... + x_62*16^62*B + // + 16*( x_1*16^0*B + x_3*16^2*B + ... + x_63*16^62*B) + // + // We use a lookup table for each i to get x_i*16^(2*i)*B + // and do four doublings to multiply by 16. + digits := x.signedRadix16() + + multiple := &affineCached{} + tmp1 := &projP1xP1{} + tmp2 := &projP2{} + + // Accumulate the odd components first + v.Set(NewIdentityPoint()) + for i := 1; i < 64; i += 2 { + basepointTable[i/2].SelectInto(multiple, digits[i]) + tmp1.AddAffine(v, multiple) + v.fromP1xP1(tmp1) + } + + // Multiply by 16 + tmp2.FromP3(v) // tmp2 = v in P2 coords + tmp1.Double(tmp2) // tmp1 = 2*v in P1xP1 coords + tmp2.FromP1xP1(tmp1) // tmp2 = 2*v in P2 coords + tmp1.Double(tmp2) // tmp1 = 4*v in P1xP1 coords + tmp2.FromP1xP1(tmp1) // tmp2 = 4*v in P2 coords + tmp1.Double(tmp2) // tmp1 = 8*v in P1xP1 coords + tmp2.FromP1xP1(tmp1) // tmp2 = 8*v in P2 coords + tmp1.Double(tmp2) // tmp1 = 16*v in P1xP1 coords + v.fromP1xP1(tmp1) // now v = 16*(odd components) + + // Accumulate the even components + for i := 0; i < 64; i += 2 { + basepointTable[i/2].SelectInto(multiple, digits[i]) + tmp1.AddAffine(v, multiple) + v.fromP1xP1(tmp1) + } + + return v +} + +// ScalarMult sets v = x * q, and returns v. +// +// The scalar multiplication is done in constant time. +func (v *Point) ScalarMult(x *Scalar, q *Point) *Point { + checkInitialized(q) + + var table projLookupTable + table.FromP3(q) + + // Write x = sum(x_i * 16^i) + // so x*Q = sum( Q*x_i*16^i ) + // = Q*x_0 + 16*(Q*x_1 + 16*( ... + Q*x_63) ... ) + // <------compute inside out--------- + // + // We use the lookup table to get the x_i*Q values + // and do four doublings to compute 16*Q + digits := x.signedRadix16() + + // Unwrap first loop iteration to save computing 16*identity + multiple := &projCached{} + tmp1 := &projP1xP1{} + tmp2 := &projP2{} + table.SelectInto(multiple, digits[63]) + + v.Set(NewIdentityPoint()) + tmp1.Add(v, multiple) // tmp1 = x_63*Q in P1xP1 coords + for i := 62; i >= 0; i-- { + tmp2.FromP1xP1(tmp1) // tmp2 = (prev) in P2 coords + tmp1.Double(tmp2) // tmp1 = 2*(prev) in P1xP1 coords + tmp2.FromP1xP1(tmp1) // tmp2 = 2*(prev) in P2 coords + tmp1.Double(tmp2) // tmp1 = 4*(prev) in P1xP1 coords + tmp2.FromP1xP1(tmp1) // tmp2 = 4*(prev) in P2 coords + tmp1.Double(tmp2) // tmp1 = 8*(prev) in P1xP1 coords + tmp2.FromP1xP1(tmp1) // tmp2 = 8*(prev) in P2 coords + tmp1.Double(tmp2) // tmp1 = 16*(prev) in P1xP1 coords + v.fromP1xP1(tmp1) // v = 16*(prev) in P3 coords + table.SelectInto(multiple, digits[i]) + tmp1.Add(v, multiple) // tmp1 = x_i*Q + 16*(prev) in P1xP1 coords + } + v.fromP1xP1(tmp1) + return v +} + +// basepointNafTable is the nafLookupTable8 for the basepoint. +// It is precomputed the first time it's used. +func basepointNafTable() *nafLookupTable8 { + basepointNafTablePrecomp.initOnce.Do(func() { + basepointNafTablePrecomp.table.FromP3(NewGeneratorPoint()) + }) + return &basepointNafTablePrecomp.table +} + +var basepointNafTablePrecomp struct { + table nafLookupTable8 + initOnce sync.Once +} + +// VarTimeDoubleScalarBaseMult sets v = a * A + b * B, where B is the canonical +// generator, and returns v. +// +// Execution time depends on the inputs. +func (v *Point) VarTimeDoubleScalarBaseMult(a *Scalar, A *Point, b *Scalar) *Point { + checkInitialized(A) + + // Similarly to the single variable-base approach, we compute + // digits and use them with a lookup table. However, because + // we are allowed to do variable-time operations, we don't + // need constant-time lookups or constant-time digit + // computations. + // + // So we use a non-adjacent form of some width w instead of + // radix 16. This is like a binary representation (one digit + // for each binary place) but we allow the digits to grow in + // magnitude up to 2^{w-1} so that the nonzero digits are as + // sparse as possible. Intuitively, this "condenses" the + // "mass" of the scalar onto sparse coefficients (meaning + // fewer additions). + + basepointNafTable := basepointNafTable() + var aTable nafLookupTable5 + aTable.FromP3(A) + // Because the basepoint is fixed, we can use a wider NAF + // corresponding to a bigger table. + aNaf := a.nonAdjacentForm(5) + bNaf := b.nonAdjacentForm(8) + + // Find the first nonzero coefficient. + i := 255 + for j := i; j >= 0; j-- { + if aNaf[j] != 0 || bNaf[j] != 0 { + break + } + } + + multA := &projCached{} + multB := &affineCached{} + tmp1 := &projP1xP1{} + tmp2 := &projP2{} + tmp2.Zero() + + // Move from high to low bits, doubling the accumulator + // at each iteration and checking whether there is a nonzero + // coefficient to look up a multiple of. + for ; i >= 0; i-- { + tmp1.Double(tmp2) + + // Only update v if we have a nonzero coeff to add in. + if aNaf[i] > 0 { + v.fromP1xP1(tmp1) + aTable.SelectInto(multA, aNaf[i]) + tmp1.Add(v, multA) + } else if aNaf[i] < 0 { + v.fromP1xP1(tmp1) + aTable.SelectInto(multA, -aNaf[i]) + tmp1.Sub(v, multA) + } + + if bNaf[i] > 0 { + v.fromP1xP1(tmp1) + basepointNafTable.SelectInto(multB, bNaf[i]) + tmp1.AddAffine(v, multB) + } else if bNaf[i] < 0 { + v.fromP1xP1(tmp1) + basepointNafTable.SelectInto(multB, -bNaf[i]) + tmp1.SubAffine(v, multB) + } + + tmp2.FromP1xP1(tmp1) + } + + v.fromP2(tmp2) + return v +} diff --git a/vendor/filippo.io/edwards25519/tables.go b/vendor/filippo.io/edwards25519/tables.go new file mode 100644 index 00000000..83234bbc --- /dev/null +++ b/vendor/filippo.io/edwards25519/tables.go @@ -0,0 +1,129 @@ +// Copyright (c) 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package edwards25519 + +import ( + "crypto/subtle" +) + +// A dynamic lookup table for variable-base, constant-time scalar muls. +type projLookupTable struct { + points [8]projCached +} + +// A precomputed lookup table for fixed-base, constant-time scalar muls. +type affineLookupTable struct { + points [8]affineCached +} + +// A dynamic lookup table for variable-base, variable-time scalar muls. +type nafLookupTable5 struct { + points [8]projCached +} + +// A precomputed lookup table for fixed-base, variable-time scalar muls. +type nafLookupTable8 struct { + points [64]affineCached +} + +// Constructors. + +// Builds a lookup table at runtime. Fast. +func (v *projLookupTable) FromP3(q *Point) { + // Goal: v.points[i] = (i+1)*Q, i.e., Q, 2Q, ..., 8Q + // This allows lookup of -8Q, ..., -Q, 0, Q, ..., 8Q + v.points[0].FromP3(q) + tmpP3 := Point{} + tmpP1xP1 := projP1xP1{} + for i := 0; i < 7; i++ { + // Compute (i+1)*Q as Q + i*Q and convert to a projCached + // This is needlessly complicated because the API has explicit + // receivers instead of creating stack objects and relying on RVO + v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.Add(q, &v.points[i]))) + } +} + +// This is not optimised for speed; fixed-base tables should be precomputed. +func (v *affineLookupTable) FromP3(q *Point) { + // Goal: v.points[i] = (i+1)*Q, i.e., Q, 2Q, ..., 8Q + // This allows lookup of -8Q, ..., -Q, 0, Q, ..., 8Q + v.points[0].FromP3(q) + tmpP3 := Point{} + tmpP1xP1 := projP1xP1{} + for i := 0; i < 7; i++ { + // Compute (i+1)*Q as Q + i*Q and convert to affineCached + v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.AddAffine(q, &v.points[i]))) + } +} + +// Builds a lookup table at runtime. Fast. +func (v *nafLookupTable5) FromP3(q *Point) { + // Goal: v.points[i] = (2*i+1)*Q, i.e., Q, 3Q, 5Q, ..., 15Q + // This allows lookup of -15Q, ..., -3Q, -Q, 0, Q, 3Q, ..., 15Q + v.points[0].FromP3(q) + q2 := Point{} + q2.Add(q, q) + tmpP3 := Point{} + tmpP1xP1 := projP1xP1{} + for i := 0; i < 7; i++ { + v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.Add(&q2, &v.points[i]))) + } +} + +// This is not optimised for speed; fixed-base tables should be precomputed. +func (v *nafLookupTable8) FromP3(q *Point) { + v.points[0].FromP3(q) + q2 := Point{} + q2.Add(q, q) + tmpP3 := Point{} + tmpP1xP1 := projP1xP1{} + for i := 0; i < 63; i++ { + v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.AddAffine(&q2, &v.points[i]))) + } +} + +// Selectors. + +// Set dest to x*Q, where -8 <= x <= 8, in constant time. +func (v *projLookupTable) SelectInto(dest *projCached, x int8) { + // Compute xabs = |x| + xmask := x >> 7 + xabs := uint8((x + xmask) ^ xmask) + + dest.Zero() + for j := 1; j <= 8; j++ { + // Set dest = j*Q if |x| = j + cond := subtle.ConstantTimeByteEq(xabs, uint8(j)) + dest.Select(&v.points[j-1], dest, cond) + } + // Now dest = |x|*Q, conditionally negate to get x*Q + dest.CondNeg(int(xmask & 1)) +} + +// Set dest to x*Q, where -8 <= x <= 8, in constant time. +func (v *affineLookupTable) SelectInto(dest *affineCached, x int8) { + // Compute xabs = |x| + xmask := x >> 7 + xabs := uint8((x + xmask) ^ xmask) + + dest.Zero() + for j := 1; j <= 8; j++ { + // Set dest = j*Q if |x| = j + cond := subtle.ConstantTimeByteEq(xabs, uint8(j)) + dest.Select(&v.points[j-1], dest, cond) + } + // Now dest = |x|*Q, conditionally negate to get x*Q + dest.CondNeg(int(xmask & 1)) +} + +// Given odd x with 0 < x < 2^4, return x*Q (in variable time). +func (v *nafLookupTable5) SelectInto(dest *projCached, x int8) { + *dest = v.points[x/2] +} + +// Given odd x with 0 < x < 2^7, return x*Q (in variable time). +func (v *nafLookupTable8) SelectInto(dest *affineCached, x int8) { + *dest = v.points[x/2] +} diff --git a/vendor/github.com/PaulSonOfLars/gotgbot/v2/custom_helpers.go b/vendor/github.com/PaulSonOfLars/gotgbot/v2/custom_helpers.go index 40676224..9eb1f3e2 100644 --- a/vendor/github.com/PaulSonOfLars/gotgbot/v2/custom_helpers.go +++ b/vendor/github.com/PaulSonOfLars/gotgbot/v2/custom_helpers.go @@ -78,6 +78,19 @@ func (im InaccessibleMessage) ToMessage() *Message { } } +// ToChat is a helper function to turn a ChatFullInfo struct into a Chat. +func (c ChatFullInfo) ToChat() Chat { + return Chat{ + Id: c.Id, + Type: c.Type, + Title: c.Title, + Username: c.Username, + FirstName: c.FirstName, + LastName: c.LastName, + IsForum: c.IsForum, + } +} + // SendMessage is a helper function to easily call Bot.SendMessage in a chat. func (c Chat) SendMessage(b *Bot, text string, opts *SendMessageOpts) (*Message, error) { return b.SendMessage(c.Id, text, opts) @@ -98,6 +111,11 @@ func (f File) URL(b *Bot, opts *RequestOpts) string { return b.FileURL(b.Token, f.FilePath, opts) } +// IsJoinRequest returns true if ChatMemberUpdated originated from a join request; either from a direct join, or from an invitelink. +func (cm ChatMemberUpdated) IsJoinRequest() bool { + return cm.ViaJoinRequest || (cm.InviteLink != nil && cm.InviteLink.CreatesJoinRequest) +} + // unmarshalMaybeInaccessibleMessage is a JSON unmarshal helper to marshal the right structs into a // MaybeInaccessibleMessage interface based on the Date field. // This method is manually maintained due to special-case handling on the Date field rather than a specific type field. diff --git a/vendor/github.com/PaulSonOfLars/gotgbot/v2/gen_helpers.go b/vendor/github.com/PaulSonOfLars/gotgbot/v2/gen_helpers.go index d3623ca1..1c1bcd3d 100644 --- a/vendor/github.com/PaulSonOfLars/gotgbot/v2/gen_helpers.go +++ b/vendor/github.com/PaulSonOfLars/gotgbot/v2/gen_helpers.go @@ -59,7 +59,7 @@ func (c Chat) ExportInviteLink(b *Bot, opts *ExportChatInviteLinkOpts) (string, } // Get Helper method for Bot.GetChat. -func (c Chat) Get(b *Bot, opts *GetChatOpts) (*Chat, error) { +func (c Chat) Get(b *Bot, opts *GetChatOpts) (*ChatFullInfo, error) { return b.GetChat(c.Id, opts) } diff --git a/vendor/github.com/PaulSonOfLars/gotgbot/v2/gen_methods.go b/vendor/github.com/PaulSonOfLars/gotgbot/v2/gen_methods.go index 555c4da3..64d11f19 100644 --- a/vendor/github.com/PaulSonOfLars/gotgbot/v2/gen_methods.go +++ b/vendor/github.com/PaulSonOfLars/gotgbot/v2/gen_methods.go @@ -279,7 +279,7 @@ type ApproveChatJoinRequestOpts struct { // ApproveChatJoinRequest (https://core.telegram.org/bots/api#approvechatjoinrequest) // // Use this method to approve a chat join request. The bot must be an administrator in the chat for this to work and must have the can_invite_users administrator right. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - userId (type int64): Unique identifier of the target user // - opts (type ApproveChatJoinRequestOpts): All optional parameters. func (bot *Bot) ApproveChatJoinRequest(chatId int64, userId int64, opts *ApproveChatJoinRequestOpts) (bool, error) { @@ -314,7 +314,7 @@ type BanChatMemberOpts struct { // BanChatMember (https://core.telegram.org/bots/api#banchatmember) // // Use this method to ban a user in a group, a supergroup or a channel. In the case of supergroups and channels, the user will not be able to return to the chat on their own using invite links, etc., unless unbanned first. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. Returns True on success. -// - chatId (type int64): Unique identifier for the target group or username of the target supergroup or channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target group // - userId (type int64): Unique identifier of the target user // - opts (type BanChatMemberOpts): All optional parameters. func (bot *Bot) BanChatMember(chatId int64, userId int64, opts *BanChatMemberOpts) (bool, error) { @@ -351,7 +351,7 @@ type BanChatSenderChatOpts struct { // BanChatSenderChat (https://core.telegram.org/bots/api#banchatsenderchat) // // Use this method to ban a channel chat in a supergroup or a channel. Until the chat is unbanned, the owner of the banned chat won't be able to send messages on behalf of any of their channels. The bot must be an administrator in the supergroup or channel for this to work and must have the appropriate administrator rights. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - senderChatId (type int64): Unique identifier of the target sender chat // - opts (type BanChatSenderChatOpts): All optional parameters. func (bot *Bot) BanChatSenderChat(chatId int64, senderChatId int64, opts *BanChatSenderChatOpts) (bool, error) { @@ -409,7 +409,7 @@ type CloseForumTopicOpts struct { // CloseForumTopic (https://core.telegram.org/bots/api#closeforumtopic) // // Use this method to close an open topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the can_manage_topics administrator rights, unless it is the creator of the topic. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) +// - chatId (type int64): Unique identifier for the target chat // - messageThreadId (type int64): Unique identifier for the target message thread of the forum topic // - opts (type CloseForumTopicOpts): All optional parameters. func (bot *Bot) CloseForumTopic(chatId int64, messageThreadId int64, opts *CloseForumTopicOpts) (bool, error) { @@ -440,7 +440,7 @@ type CloseGeneralForumTopicOpts struct { // CloseGeneralForumTopic (https://core.telegram.org/bots/api#closegeneralforumtopic) // // Use this method to close an open 'General' topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the can_manage_topics administrator rights. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) +// - chatId (type int64): Unique identifier for the target chat // - opts (type CloseGeneralForumTopicOpts): All optional parameters. func (bot *Bot) CloseGeneralForumTopic(chatId int64, opts *CloseGeneralForumTopicOpts) (bool, error) { v := map[string]string{} @@ -476,7 +476,7 @@ type CopyMessageOpts struct { ProtectContent bool // Description of the message to reply to ReplyParameters *ReplyParameters - // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. + // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user ReplyMarkup ReplyMarkup // RequestOpts are an additional optional field to configure timeouts for individual requests RequestOpts *RequestOpts @@ -485,8 +485,8 @@ type CopyMessageOpts struct { // CopyMessage (https://core.telegram.org/bots/api#copymessage) // // Use this method to copy messages of any kind. Service messages, giveaway messages, giveaway winners messages, and invoice messages can't be copied. A quiz poll can be copied only if the value of the field correct_option_id is known to the bot. The method is analogous to the method forwardMessage, but the copied message doesn't have a link to the original message. Returns the MessageId of the sent message on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) -// - fromChatId (type int64): Unique identifier for the chat where the original message was sent (or channel username in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat +// - fromChatId (type int64): Unique identifier for the chat where the original message was sent // - messageId (type int64): Message identifier in the chat specified in from_chat_id // - opts (type CopyMessageOpts): All optional parameters. func (bot *Bot) CopyMessage(chatId int64, fromChatId int64, messageId int64, opts *CopyMessageOpts) (*MessageId, error) { @@ -558,8 +558,8 @@ type CopyMessagesOpts struct { // CopyMessages (https://core.telegram.org/bots/api#copymessages) // // Use this method to copy messages of any kind. If some of the specified messages can't be found or copied, they are skipped. Service messages, giveaway messages, giveaway winners messages, and invoice messages can't be copied. A quiz poll can be copied only if the value of the field correct_option_id is known to the bot. The method is analogous to the method forwardMessages, but the copied messages don't have a link to the original message. Album grouping is kept for copied messages. On success, an array of MessageId of the sent messages is returned. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) -// - fromChatId (type int64): Unique identifier for the chat where the original messages were sent (or channel username in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat +// - fromChatId (type int64): Unique identifier for the chat where the original messages were sent // - messageIds (type []int64): A JSON-serialized list of 1-100 identifiers of messages in the chat from_chat_id to copy. The identifiers must be specified in a strictly increasing order. // - opts (type CopyMessagesOpts): All optional parameters. func (bot *Bot) CopyMessages(chatId int64, fromChatId int64, messageIds []int64, opts *CopyMessagesOpts) ([]MessageId, error) { @@ -613,7 +613,7 @@ type CreateChatInviteLinkOpts struct { // CreateChatInviteLink (https://core.telegram.org/bots/api#createchatinvitelink) // // Use this method to create an additional invite link for a chat. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. The link can be revoked using the method revokeChatInviteLink. Returns the new invite link as ChatInviteLink object. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - opts (type CreateChatInviteLinkOpts): All optional parameters. func (bot *Bot) CreateChatInviteLink(chatId int64, opts *CreateChatInviteLinkOpts) (*ChatInviteLink, error) { v := map[string]string{} @@ -656,7 +656,7 @@ type CreateForumTopicOpts struct { // CreateForumTopic (https://core.telegram.org/bots/api#createforumtopic) // // Use this method to create a topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the can_manage_topics administrator rights. Returns information about the created topic as a ForumTopic object. -// - chatId (type int64): Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) +// - chatId (type int64): Unique identifier for the target chat // - name (type string): Topic name, 1-128 characters // - opts (type CreateForumTopicOpts): All optional parameters. func (bot *Bot) CreateForumTopic(chatId int64, name string, opts *CreateForumTopicOpts) (*ForumTopic, error) { @@ -854,7 +854,7 @@ type DeclineChatJoinRequestOpts struct { // DeclineChatJoinRequest (https://core.telegram.org/bots/api#declinechatjoinrequest) // // Use this method to decline a chat join request. The bot must be an administrator in the chat for this to work and must have the can_invite_users administrator right. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - userId (type int64): Unique identifier of the target user // - opts (type DeclineChatJoinRequestOpts): All optional parameters. func (bot *Bot) DeclineChatJoinRequest(chatId int64, userId int64, opts *DeclineChatJoinRequestOpts) (bool, error) { @@ -885,7 +885,7 @@ type DeleteChatPhotoOpts struct { // DeleteChatPhoto (https://core.telegram.org/bots/api#deletechatphoto) // // Use this method to delete a chat photo. Photos can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - opts (type DeleteChatPhotoOpts): All optional parameters. func (bot *Bot) DeleteChatPhoto(chatId int64, opts *DeleteChatPhotoOpts) (bool, error) { v := map[string]string{} @@ -914,7 +914,7 @@ type DeleteChatStickerSetOpts struct { // DeleteChatStickerSet (https://core.telegram.org/bots/api#deletechatstickerset) // // Use this method to delete a group sticker set from a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. Use the field can_set_sticker_set optionally returned in getChat requests to check if the bot can use this method. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) +// - chatId (type int64): Unique identifier for the target chat // - opts (type DeleteChatStickerSetOpts): All optional parameters. func (bot *Bot) DeleteChatStickerSet(chatId int64, opts *DeleteChatStickerSetOpts) (bool, error) { v := map[string]string{} @@ -943,7 +943,7 @@ type DeleteForumTopicOpts struct { // DeleteForumTopic (https://core.telegram.org/bots/api#deleteforumtopic) // // Use this method to delete a forum topic along with all its messages in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the can_delete_messages administrator rights. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) +// - chatId (type int64): Unique identifier for the target chat // - messageThreadId (type int64): Unique identifier for the target message thread of the forum topic // - opts (type DeleteForumTopicOpts): All optional parameters. func (bot *Bot) DeleteForumTopic(chatId int64, messageThreadId int64, opts *DeleteForumTopicOpts) (bool, error) { @@ -984,7 +984,7 @@ type DeleteMessageOpts struct { // - If the bot has can_delete_messages permission in a supergroup or a channel, it can delete any message there. // // Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - messageId (type int64): Identifier of the message to delete // - opts (type DeleteMessageOpts): All optional parameters. func (bot *Bot) DeleteMessage(chatId int64, messageId int64, opts *DeleteMessageOpts) (bool, error) { @@ -1015,7 +1015,7 @@ type DeleteMessagesOpts struct { // DeleteMessages (https://core.telegram.org/bots/api#deletemessages) // // Use this method to delete multiple messages simultaneously. If some of the specified messages can't be found, they are skipped. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - messageIds (type []int64): A JSON-serialized list of 1-100 identifiers of messages to delete. See deleteMessage for limitations on which messages can be deleted // - opts (type DeleteMessagesOpts): All optional parameters. func (bot *Bot) DeleteMessages(chatId int64, messageIds []int64, opts *DeleteMessagesOpts) (bool, error) { @@ -1189,7 +1189,7 @@ type EditChatInviteLinkOpts struct { // EditChatInviteLink (https://core.telegram.org/bots/api#editchatinvitelink) // // Use this method to edit a non-primary invite link created by the bot. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. Returns the edited invite link as a ChatInviteLink object. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - inviteLink (type string): The invite link to edit // - opts (type EditChatInviteLinkOpts): All optional parameters. func (bot *Bot) EditChatInviteLink(chatId int64, inviteLink string, opts *EditChatInviteLinkOpts) (*ChatInviteLink, error) { @@ -1234,7 +1234,7 @@ type EditForumTopicOpts struct { // EditForumTopic (https://core.telegram.org/bots/api#editforumtopic) // // Use this method to edit name and icon of a topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights, unless it is the creator of the topic. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) +// - chatId (type int64): Unique identifier for the target chat // - messageThreadId (type int64): Unique identifier for the target message thread of the forum topic // - opts (type EditForumTopicOpts): All optional parameters. func (bot *Bot) EditForumTopic(chatId int64, messageThreadId int64, opts *EditForumTopicOpts) (bool, error) { @@ -1271,7 +1271,7 @@ type EditGeneralForumTopicOpts struct { // EditGeneralForumTopic (https://core.telegram.org/bots/api#editgeneralforumtopic) // // Use this method to edit the name of the 'General' topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) +// - chatId (type int64): Unique identifier for the target chat // - name (type string): New topic name, 1-128 characters // - opts (type EditGeneralForumTopicOpts): All optional parameters. func (bot *Bot) EditGeneralForumTopic(chatId int64, name string, opts *EditGeneralForumTopicOpts) (bool, error) { @@ -1295,7 +1295,7 @@ func (bot *Bot) EditGeneralForumTopic(chatId int64, name string, opts *EditGener // EditMessageCaptionOpts is the set of optional fields for Bot.EditMessageCaption. type EditMessageCaptionOpts struct { - // Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format @channelusername) + // Required if inline_message_id is not specified. Unique identifier for the target chat ChatId int64 // Required if inline_message_id is not specified. Identifier of the message to edit MessageId int64 @@ -1367,12 +1367,14 @@ func (bot *Bot) EditMessageCaption(opts *EditMessageCaptionOpts) (*Message, bool // EditMessageLiveLocationOpts is the set of optional fields for Bot.EditMessageLiveLocation. type EditMessageLiveLocationOpts struct { - // Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format @channelusername) + // Required if inline_message_id is not specified. Unique identifier for the target chat ChatId int64 // Required if inline_message_id is not specified. Identifier of the message to edit MessageId int64 // Required if chat_id and message_id are not specified. Identifier of the inline message InlineMessageId string + // New period in seconds during which the location can be updated, starting from the message send date. If 0x7FFFFFFF is specified, then the location can be updated forever. Otherwise, the new value must not exceed the current live_period by more than a day, and the live location expiration date must remain within the next 90 days. If not specified, then live_period remains unchanged + LivePeriod *int64 // The radius of uncertainty for the location, measured in meters; 0-1500 HorizontalAccuracy float64 // Direction in which the user is moving, in degrees. Must be between 1 and 360 if specified. @@ -1403,6 +1405,9 @@ func (bot *Bot) EditMessageLiveLocation(latitude float64, longitude float64, opt v["message_id"] = strconv.FormatInt(opts.MessageId, 10) } v["inline_message_id"] = opts.InlineMessageId + if opts.LivePeriod != nil { + v["live_period"] = strconv.FormatInt(*opts.LivePeriod, 10) + } if opts.HorizontalAccuracy != 0.0 { v["horizontal_accuracy"] = strconv.FormatFloat(opts.HorizontalAccuracy, 'f', -1, 64) } @@ -1443,7 +1448,7 @@ func (bot *Bot) EditMessageLiveLocation(latitude float64, longitude float64, opt // EditMessageMediaOpts is the set of optional fields for Bot.EditMessageMedia. type EditMessageMediaOpts struct { - // Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format @channelusername) + // Required if inline_message_id is not specified. Unique identifier for the target chat ChatId int64 // Required if inline_message_id is not specified. Identifier of the message to edit MessageId int64 @@ -1507,7 +1512,7 @@ func (bot *Bot) EditMessageMedia(media InputMedia, opts *EditMessageMediaOpts) ( // EditMessageReplyMarkupOpts is the set of optional fields for Bot.EditMessageReplyMarkup. type EditMessageReplyMarkupOpts struct { - // Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format @channelusername) + // Required if inline_message_id is not specified. Unique identifier for the target chat ChatId int64 // Required if inline_message_id is not specified. Identifier of the message to edit MessageId int64 @@ -1564,7 +1569,7 @@ func (bot *Bot) EditMessageReplyMarkup(opts *EditMessageReplyMarkupOpts) (*Messa // EditMessageTextOpts is the set of optional fields for Bot.EditMessageText. type EditMessageTextOpts struct { - // Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format @channelusername) + // Required if inline_message_id is not specified. Unique identifier for the target chat ChatId int64 // Required if inline_message_id is not specified. Identifier of the message to edit MessageId int64 @@ -1651,7 +1656,7 @@ type ExportChatInviteLinkOpts struct { // ExportChatInviteLink (https://core.telegram.org/bots/api#exportchatinvitelink) // // Use this method to generate a new primary invite link for a chat; any previously generated primary link is revoked. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. Returns the new invite link as String on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - opts (type ExportChatInviteLinkOpts): All optional parameters. func (bot *Bot) ExportChatInviteLink(chatId int64, opts *ExportChatInviteLinkOpts) (string, error) { v := map[string]string{} @@ -1686,8 +1691,8 @@ type ForwardMessageOpts struct { // ForwardMessage (https://core.telegram.org/bots/api#forwardmessage) // // Use this method to forward messages of any kind. Service messages and messages with protected content can't be forwarded. On success, the sent Message is returned. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) -// - fromChatId (type int64): Unique identifier for the chat where the original message was sent (or channel username in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat +// - fromChatId (type int64): Unique identifier for the chat where the original message was sent // - messageId (type int64): Message identifier in the chat specified in from_chat_id // - opts (type ForwardMessageOpts): All optional parameters. func (bot *Bot) ForwardMessage(chatId int64, fromChatId int64, messageId int64, opts *ForwardMessageOpts) (*Message, error) { @@ -1732,8 +1737,8 @@ type ForwardMessagesOpts struct { // ForwardMessages (https://core.telegram.org/bots/api#forwardmessages) // // Use this method to forward multiple messages of any kind. If some of the specified messages can't be found or forwarded, they are skipped. Service messages and messages with protected content can't be forwarded. Album grouping is kept for forwarded messages. On success, an array of MessageId of the sent messages is returned. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) -// - fromChatId (type int64): Unique identifier for the chat where the original messages were sent (or channel username in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat +// - fromChatId (type int64): Unique identifier for the chat where the original messages were sent // - messageIds (type []int64): A JSON-serialized list of 1-100 identifiers of messages in the chat from_chat_id to forward. The identifiers must be specified in a strictly increasing order. // - opts (type ForwardMessagesOpts): All optional parameters. func (bot *Bot) ForwardMessages(chatId int64, fromChatId int64, messageIds []int64, opts *ForwardMessagesOpts) ([]MessageId, error) { @@ -1806,10 +1811,10 @@ type GetChatOpts struct { // GetChat (https://core.telegram.org/bots/api#getchat) // -// Use this method to get up to date information about the chat. Returns a Chat object on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername) +// Use this method to get up-to-date information about the chat. Returns a ChatFullInfo object on success. +// - chatId (type int64): Unique identifier for the target chat // - opts (type GetChatOpts): All optional parameters. -func (bot *Bot) GetChat(chatId int64, opts *GetChatOpts) (*Chat, error) { +func (bot *Bot) GetChat(chatId int64, opts *GetChatOpts) (*ChatFullInfo, error) { v := map[string]string{} v["chat_id"] = strconv.FormatInt(chatId, 10) @@ -1823,7 +1828,7 @@ func (bot *Bot) GetChat(chatId int64, opts *GetChatOpts) (*Chat, error) { return nil, err } - var c Chat + var c ChatFullInfo return &c, json.Unmarshal(r, &c) } @@ -1836,7 +1841,7 @@ type GetChatAdministratorsOpts struct { // GetChatAdministrators (https://core.telegram.org/bots/api#getchatadministrators) // // Use this method to get a list of administrators in a chat, which aren't bots. Returns an Array of ChatMember objects. -// - chatId (type int64): Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - opts (type GetChatAdministratorsOpts): All optional parameters. func (bot *Bot) GetChatAdministrators(chatId int64, opts *GetChatAdministratorsOpts) ([]ChatMember, error) { v := map[string]string{} @@ -1864,7 +1869,7 @@ type GetChatMemberOpts struct { // GetChatMember (https://core.telegram.org/bots/api#getchatmember) // // Use this method to get information about a member of a chat. The method is only guaranteed to work for other users if the bot is an administrator in the chat. Returns a ChatMember object on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - userId (type int64): Unique identifier of the target user // - opts (type GetChatMemberOpts): All optional parameters. func (bot *Bot) GetChatMember(chatId int64, userId int64, opts *GetChatMemberOpts) (ChatMember, error) { @@ -1894,7 +1899,7 @@ type GetChatMemberCountOpts struct { // GetChatMemberCount (https://core.telegram.org/bots/api#getchatmembercount) // // Use this method to get the number of members in a chat. Returns Int on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - opts (type GetChatMemberCountOpts): All optional parameters. func (bot *Bot) GetChatMemberCount(chatId int64, opts *GetChatMemberCountOpts) (int64, error) { v := map[string]string{} @@ -2368,7 +2373,7 @@ type GetUserChatBoostsOpts struct { // GetUserChatBoosts (https://core.telegram.org/bots/api#getuserchatboosts) // // Use this method to get the list of boosts added to a chat by a user. Requires administrator rights in the chat. Returns a UserChatBoosts object. -// - chatId (type int64): Unique identifier for the chat or username of the channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the chat // - userId (type int64): Unique identifier of the target user // - opts (type GetUserChatBoostsOpts): All optional parameters. func (bot *Bot) GetUserChatBoosts(chatId int64, userId int64, opts *GetUserChatBoostsOpts) (*UserChatBoosts, error) { @@ -2467,7 +2472,7 @@ type HideGeneralForumTopicOpts struct { // HideGeneralForumTopic (https://core.telegram.org/bots/api#hidegeneralforumtopic) // // Use this method to hide the 'General' topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the can_manage_topics administrator rights. The topic will be automatically closed if it was open. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) +// - chatId (type int64): Unique identifier for the target chat // - opts (type HideGeneralForumTopicOpts): All optional parameters. func (bot *Bot) HideGeneralForumTopic(chatId int64, opts *HideGeneralForumTopicOpts) (bool, error) { v := map[string]string{} @@ -2496,7 +2501,7 @@ type LeaveChatOpts struct { // LeaveChat (https://core.telegram.org/bots/api#leavechat) // // Use this method for your bot to leave a group, supergroup or channel. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - opts (type LeaveChatOpts): All optional parameters. func (bot *Bot) LeaveChat(chatId int64, opts *LeaveChatOpts) (bool, error) { v := map[string]string{} @@ -2554,7 +2559,7 @@ type PinChatMessageOpts struct { // PinChatMessage (https://core.telegram.org/bots/api#pinchatmessage) // // Use this method to add a message to the list of pinned messages in a chat. If the chat is not a private chat, the bot must be an administrator in the chat for this to work and must have the 'can_pin_messages' administrator right in a supergroup or 'can_edit_messages' administrator right in a channel. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - messageId (type int64): Identifier of a message to pin // - opts (type PinChatMessageOpts): All optional parameters. func (bot *Bot) PinChatMessage(chatId int64, messageId int64, opts *PinChatMessageOpts) (bool, error) { @@ -2599,7 +2604,7 @@ type PromoteChatMemberOpts struct { CanInviteUsers bool // Pass True if the administrator can post stories to the chat CanPostStories bool - // Pass True if the administrator can edit stories posted by other users + // Pass True if the administrator can edit stories posted by other users, post stories to the chat page, pin chat stories, and access the chat's story archive CanEditStories bool // Pass True if the administrator can delete stories posted by other users CanDeleteStories bool @@ -2618,7 +2623,7 @@ type PromoteChatMemberOpts struct { // PromoteChatMember (https://core.telegram.org/bots/api#promotechatmember) // // Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. Pass False for all boolean parameters to demote a user. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - userId (type int64): Unique identifier of the target user // - opts (type PromoteChatMemberOpts): All optional parameters. func (bot *Bot) PromoteChatMember(chatId int64, userId int64, opts *PromoteChatMemberOpts) (bool, error) { @@ -2666,7 +2671,7 @@ type ReopenForumTopicOpts struct { // ReopenForumTopic (https://core.telegram.org/bots/api#reopenforumtopic) // // Use this method to reopen a closed topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the can_manage_topics administrator rights, unless it is the creator of the topic. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) +// - chatId (type int64): Unique identifier for the target chat // - messageThreadId (type int64): Unique identifier for the target message thread of the forum topic // - opts (type ReopenForumTopicOpts): All optional parameters. func (bot *Bot) ReopenForumTopic(chatId int64, messageThreadId int64, opts *ReopenForumTopicOpts) (bool, error) { @@ -2697,7 +2702,7 @@ type ReopenGeneralForumTopicOpts struct { // ReopenGeneralForumTopic (https://core.telegram.org/bots/api#reopengeneralforumtopic) // // Use this method to reopen a closed 'General' topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the can_manage_topics administrator rights. The topic will be automatically unhidden if it was hidden. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) +// - chatId (type int64): Unique identifier for the target chat // - opts (type ReopenGeneralForumTopicOpts): All optional parameters. func (bot *Bot) ReopenGeneralForumTopic(chatId int64, opts *ReopenGeneralForumTopicOpts) (bool, error) { v := map[string]string{} @@ -2770,7 +2775,7 @@ type RestrictChatMemberOpts struct { // RestrictChatMember (https://core.telegram.org/bots/api#restrictchatmember) // // Use this method to restrict a user in a supergroup. The bot must be an administrator in the supergroup for this to work and must have the appropriate administrator rights. Pass True for all permissions to lift restrictions from a user. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) +// - chatId (type int64): Unique identifier for the target chat // - userId (type int64): Unique identifier of the target user // - permissions (type ChatPermissions): A JSON-serialized object for new user permissions // - opts (type RestrictChatMemberOpts): All optional parameters. @@ -2813,7 +2818,7 @@ type RevokeChatInviteLinkOpts struct { // RevokeChatInviteLink (https://core.telegram.org/bots/api#revokechatinvitelink) // // Use this method to revoke an invite link created by the bot. If the primary link is revoked, a new link is automatically generated. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. Returns the revoked invite link as ChatInviteLink object. -// - chatId (type int64): Unique identifier of the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier of the target chat // - inviteLink (type string): The invite link to revoke // - opts (type RevokeChatInviteLinkOpts): All optional parameters. func (bot *Bot) RevokeChatInviteLink(chatId int64, inviteLink string, opts *RevokeChatInviteLinkOpts) (*ChatInviteLink, error) { @@ -2863,7 +2868,7 @@ type SendAnimationOpts struct { ProtectContent bool // Description of the message to reply to ReplyParameters *ReplyParameters - // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user. Not supported for messages sent on behalf of a business account. + // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user ReplyMarkup ReplyMarkup // RequestOpts are an additional optional field to configure timeouts for individual requests RequestOpts *RequestOpts @@ -2872,7 +2877,7 @@ type SendAnimationOpts struct { // SendAnimation (https://core.telegram.org/bots/api#sendanimation) // // Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - animation (type InputFile): Animation to send. Pass a file_id as String to send an animation that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get an animation from the Internet, or upload a new animation using multipart/form-data. More information on Sending Files: https://core.telegram.org/bots/api#sending-files // - opts (type SendAnimationOpts): All optional parameters. func (bot *Bot) SendAnimation(chatId int64, animation InputFile, opts *SendAnimationOpts) (*Message, error) { @@ -3003,7 +3008,7 @@ type SendAudioOpts struct { ProtectContent bool // Description of the message to reply to ReplyParameters *ReplyParameters - // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user. Not supported for messages sent on behalf of a business account. + // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user ReplyMarkup ReplyMarkup // RequestOpts are an additional optional field to configure timeouts for individual requests RequestOpts *RequestOpts @@ -3013,7 +3018,7 @@ type SendAudioOpts struct { // // Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .MP3 or .M4A format. On success, the sent Message is returned. Bots can currently send audio files of up to 50 MB in size, this limit may be changed in the future. // For sending voice messages, use the sendVoice method instead. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - audio (type InputFile): Audio file to send. Pass a file_id as String to send an audio file that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get an audio file from the Internet, or upload a new one using multipart/form-data. More information on Sending Files: https://core.telegram.org/bots/api#sending-files // - opts (type SendAudioOpts): All optional parameters. func (bot *Bot) SendAudio(chatId int64, audio InputFile, opts *SendAudioOpts) (*Message, error) { @@ -3127,7 +3132,7 @@ type SendChatActionOpts struct { // // Use this method when you need to tell the user that something is happening on the bot's side. The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear its typing status). Returns True on success. // We only recommend using this method when a response from the bot will take a noticeable amount of time to arrive. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - action (type string): Type of action to broadcast. Choose one, depending on what the user is about to receive: typing for text messages, upload_photo for photos, record_video or upload_video for videos, record_voice or upload_voice for voice notes, upload_document for general files, choose_sticker for stickers, find_location for location data, record_video_note or upload_video_note for video notes. // - opts (type SendChatActionOpts): All optional parameters. func (bot *Bot) SendChatAction(chatId int64, action string, opts *SendChatActionOpts) (bool, error) { @@ -3171,7 +3176,7 @@ type SendContactOpts struct { ProtectContent bool // Description of the message to reply to ReplyParameters *ReplyParameters - // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user. Not supported for messages sent on behalf of a business account. + // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user ReplyMarkup ReplyMarkup // RequestOpts are an additional optional field to configure timeouts for individual requests RequestOpts *RequestOpts @@ -3180,7 +3185,7 @@ type SendContactOpts struct { // SendContact (https://core.telegram.org/bots/api#sendcontact) // // Use this method to send phone contacts. On success, the sent Message is returned. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - phoneNumber (type string): Contact's phone number // - firstName (type string): Contact's first name // - opts (type SendContactOpts): All optional parameters. @@ -3242,7 +3247,7 @@ type SendDiceOpts struct { ProtectContent bool // Description of the message to reply to ReplyParameters *ReplyParameters - // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user. Not supported for messages sent on behalf of a business account. + // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user ReplyMarkup ReplyMarkup // RequestOpts are an additional optional field to configure timeouts for individual requests RequestOpts *RequestOpts @@ -3251,7 +3256,7 @@ type SendDiceOpts struct { // SendDice (https://core.telegram.org/bots/api#senddice) // // Use this method to send an animated emoji that will display a random value. On success, the sent Message is returned. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - opts (type SendDiceOpts): All optional parameters. func (bot *Bot) SendDice(chatId int64, opts *SendDiceOpts) (*Message, error) { v := map[string]string{} @@ -3316,7 +3321,7 @@ type SendDocumentOpts struct { ProtectContent bool // Description of the message to reply to ReplyParameters *ReplyParameters - // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user. Not supported for messages sent on behalf of a business account. + // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user ReplyMarkup ReplyMarkup // RequestOpts are an additional optional field to configure timeouts for individual requests RequestOpts *RequestOpts @@ -3325,7 +3330,7 @@ type SendDocumentOpts struct { // SendDocument (https://core.telegram.org/bots/api#senddocument) // // Use this method to send general files. On success, the sent Message is returned. Bots can currently send files of any type of up to 50 MB in size, this limit may be changed in the future. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - document (type InputFile): File to send. Pass a file_id as String to send a file that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data. More information on Sending Files: https://core.telegram.org/bots/api#sending-files // - opts (type SendDocumentOpts): All optional parameters. func (bot *Bot) SendDocument(chatId int64, document InputFile, opts *SendDocumentOpts) (*Message, error) { @@ -3433,7 +3438,7 @@ type SendGameOpts struct { ProtectContent bool // Description of the message to reply to ReplyParameters *ReplyParameters - // A JSON-serialized object for an inline keyboard. If empty, one 'Play game_title' button will be shown. If not empty, the first button must launch the game. Not supported for messages sent on behalf of a business account. + // A JSON-serialized object for an inline keyboard. If empty, one 'Play game_title' button will be shown. If not empty, the first button must launch the game. ReplyMarkup InlineKeyboardMarkup // RequestOpts are an additional optional field to configure timeouts for individual requests RequestOpts *RequestOpts @@ -3533,7 +3538,7 @@ type SendInvoiceOpts struct { // SendInvoice (https://core.telegram.org/bots/api#sendinvoice) // // Use this method to send invoices. On success, the sent Message is returned. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - title (type string): Product name, 1-32 characters // - description (type string): Product description, 1-255 characters // - payload (type string): Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes. @@ -3627,7 +3632,7 @@ type SendLocationOpts struct { MessageThreadId int64 // The radius of uncertainty for the location, measured in meters; 0-1500 HorizontalAccuracy float64 - // Period in seconds for which the location will be updated (see Live Locations, should be between 60 and 86400. + // Period in seconds during which the location will be updated (see Live Locations, should be between 60 and 86400, or 0x7FFFFFFF for live locations that can be edited indefinitely. LivePeriod int64 // For live locations, a direction in which the user is moving, in degrees. Must be between 1 and 360 if specified. Heading int64 @@ -3639,7 +3644,7 @@ type SendLocationOpts struct { ProtectContent bool // Description of the message to reply to ReplyParameters *ReplyParameters - // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user. Not supported for messages sent on behalf of a business account. + // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user ReplyMarkup ReplyMarkup // RequestOpts are an additional optional field to configure timeouts for individual requests RequestOpts *RequestOpts @@ -3648,7 +3653,7 @@ type SendLocationOpts struct { // SendLocation (https://core.telegram.org/bots/api#sendlocation) // // Use this method to send point on the map. On success, the sent Message is returned. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - latitude (type float64): Latitude of the location // - longitude (type float64): Longitude of the location // - opts (type SendLocationOpts): All optional parameters. @@ -3725,7 +3730,7 @@ type SendMediaGroupOpts struct { // SendMediaGroup (https://core.telegram.org/bots/api#sendmediagroup) // // Use this method to send a group of photos, videos, documents or audios as an album. Documents and audio files can be only grouped in an album with messages of the same type. On success, an array of Messages that were sent is returned. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - media (type []InputMedia): A JSON-serialized array describing messages to be sent, must include 2-10 items // - opts (type SendMediaGroupOpts): All optional parameters. func (bot *Bot) SendMediaGroup(chatId int64, media []InputMedia, opts *SendMediaGroupOpts) ([]Message, error) { @@ -3795,7 +3800,7 @@ type SendMessageOpts struct { ProtectContent bool // Description of the message to reply to ReplyParameters *ReplyParameters - // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user. Not supported for messages sent on behalf of a business account. + // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user ReplyMarkup ReplyMarkup // RequestOpts are an additional optional field to configure timeouts for individual requests RequestOpts *RequestOpts @@ -3804,7 +3809,7 @@ type SendMessageOpts struct { // SendMessage (https://core.telegram.org/bots/api#sendmessage) // // Use this method to send text messages. On success, the sent Message is returned. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - text (type string): Text of the message to be sent, 1-4096 characters after entities parsing // - opts (type SendMessageOpts): All optional parameters. func (bot *Bot) SendMessage(chatId int64, text string, opts *SendMessageOpts) (*Message, error) { @@ -3883,7 +3888,7 @@ type SendPhotoOpts struct { ProtectContent bool // Description of the message to reply to ReplyParameters *ReplyParameters - // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user. Not supported for messages sent on behalf of a business account. + // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user ReplyMarkup ReplyMarkup // RequestOpts are an additional optional field to configure timeouts for individual requests RequestOpts *RequestOpts @@ -3892,7 +3897,7 @@ type SendPhotoOpts struct { // SendPhoto (https://core.telegram.org/bots/api#sendphoto) // // Use this method to send photos. On success, the sent Message is returned. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - photo (type InputFile): Photo to send. Pass a file_id as String to send a photo that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a photo from the Internet, or upload a new photo using multipart/form-data. The photo must be at most 10 MB in size. The photo's width and height must not exceed 10000 in total. Width and height ratio must be at most 20. More information on Sending Files: https://core.telegram.org/bots/api#sending-files // - opts (type SendPhotoOpts): All optional parameters. func (bot *Bot) SendPhoto(chatId int64, photo InputFile, opts *SendPhotoOpts) (*Message, error) { @@ -3973,6 +3978,10 @@ type SendPollOpts struct { BusinessConnectionId string // Unique identifier for the target message thread (topic) of the forum; for forum supergroups only MessageThreadId int64 + // Mode for parsing entities in the question. See formatting options for more details. Currently, only custom emoji entities are allowed + QuestionParseMode string + // A JSON-serialized list of special entities that appear in the poll question. It can be specified instead of question_parse_mode + QuestionEntities []MessageEntity // True, if the poll needs to be anonymous, defaults to True IsAnonymous bool // Poll type, "quiz" or "regular", defaults to "regular" @@ -3985,7 +3994,7 @@ type SendPollOpts struct { Explanation string // Mode for parsing entities in the explanation. See formatting options for more details. ExplanationParseMode string - // A JSON-serialized list of special entities that appear in the poll explanation, which can be specified instead of parse_mode + // A JSON-serialized list of special entities that appear in the poll explanation. It can be specified instead of explanation_parse_mode ExplanationEntities []MessageEntity // Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with close_date. OpenPeriod int64 @@ -3999,7 +4008,7 @@ type SendPollOpts struct { ProtectContent bool // Description of the message to reply to ReplyParameters *ReplyParameters - // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user. Not supported for messages sent on behalf of a business account. + // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user ReplyMarkup ReplyMarkup // RequestOpts are an additional optional field to configure timeouts for individual requests RequestOpts *RequestOpts @@ -4008,11 +4017,11 @@ type SendPollOpts struct { // SendPoll (https://core.telegram.org/bots/api#sendpoll) // // Use this method to send a native poll. On success, the sent Message is returned. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - question (type string): Poll question, 1-300 characters -// - options (type []string): A JSON-serialized list of answer options, 2-10 strings 1-100 characters each +// - options (type []InputPollOption): A JSON-serialized list of 2-10 answer options // - opts (type SendPollOpts): All optional parameters. -func (bot *Bot) SendPoll(chatId int64, question string, options []string, opts *SendPollOpts) (*Message, error) { +func (bot *Bot) SendPoll(chatId int64, question string, options []InputPollOption, opts *SendPollOpts) (*Message, error) { v := map[string]string{} v["chat_id"] = strconv.FormatInt(chatId, 10) v["question"] = question @@ -4028,6 +4037,14 @@ func (bot *Bot) SendPoll(chatId int64, question string, options []string, opts * if opts.MessageThreadId != 0 { v["message_thread_id"] = strconv.FormatInt(opts.MessageThreadId, 10) } + v["question_parse_mode"] = opts.QuestionParseMode + if opts.QuestionEntities != nil { + bs, err := json.Marshal(opts.QuestionEntities) + if err != nil { + return nil, fmt.Errorf("failed to marshal field question_entities: %w", err) + } + v["question_entities"] = string(bs) + } v["is_anonymous"] = strconv.FormatBool(opts.IsAnonymous) v["type"] = opts.Type v["allows_multiple_answers"] = strconv.FormatBool(opts.AllowsMultipleAnswers) @@ -4097,7 +4114,7 @@ type SendStickerOpts struct { ProtectContent bool // Description of the message to reply to ReplyParameters *ReplyParameters - // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. Not supported for messages sent on behalf of a business account. + // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user ReplyMarkup ReplyMarkup // RequestOpts are an additional optional field to configure timeouts for individual requests RequestOpts *RequestOpts @@ -4106,7 +4123,7 @@ type SendStickerOpts struct { // SendSticker (https://core.telegram.org/bots/api#sendsticker) // // Use this method to send static .WEBP, animated .TGS, or video .WEBM stickers. On success, the sent Message is returned. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - sticker (type InputFile): Sticker to send. Pass a file_id as String to send a file that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a .WEBP sticker from the Internet, or upload a new .WEBP, .TGS, or .WEBM sticker using multipart/form-data. More information on Sending Files: https://core.telegram.org/bots/api#sending-files. Video and animated stickers can't be sent via an HTTP URL. // - opts (type SendStickerOpts): All optional parameters. func (bot *Bot) SendSticker(chatId int64, sticker InputFile, opts *SendStickerOpts) (*Message, error) { @@ -4192,7 +4209,7 @@ type SendVenueOpts struct { ProtectContent bool // Description of the message to reply to ReplyParameters *ReplyParameters - // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user. Not supported for messages sent on behalf of a business account. + // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user ReplyMarkup ReplyMarkup // RequestOpts are an additional optional field to configure timeouts for individual requests RequestOpts *RequestOpts @@ -4201,7 +4218,7 @@ type SendVenueOpts struct { // SendVenue (https://core.telegram.org/bots/api#sendvenue) // // Use this method to send information about a venue. On success, the sent Message is returned. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - latitude (type float64): Latitude of the venue // - longitude (type float64): Longitude of the venue // - title (type string): Name of the venue @@ -4285,7 +4302,7 @@ type SendVideoOpts struct { ProtectContent bool // Description of the message to reply to ReplyParameters *ReplyParameters - // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user. Not supported for messages sent on behalf of a business account. + // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user ReplyMarkup ReplyMarkup // RequestOpts are an additional optional field to configure timeouts for individual requests RequestOpts *RequestOpts @@ -4294,7 +4311,7 @@ type SendVideoOpts struct { // SendVideo (https://core.telegram.org/bots/api#sendvideo) // // Use this method to send video files, Telegram clients support MPEG4 videos (other formats may be sent as Document). On success, the sent Message is returned. Bots can currently send video files of up to 50 MB in size, this limit may be changed in the future. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - video (type InputFile): Video to send. Pass a file_id as String to send a video that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a video from the Internet, or upload a new video using multipart/form-data. More information on Sending Files: https://core.telegram.org/bots/api#sending-files // - opts (type SendVideoOpts): All optional parameters. func (bot *Bot) SendVideo(chatId int64, video InputFile, opts *SendVideoOpts) (*Message, error) { @@ -4418,7 +4435,7 @@ type SendVideoNoteOpts struct { ProtectContent bool // Description of the message to reply to ReplyParameters *ReplyParameters - // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user. Not supported for messages sent on behalf of a business account. + // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user ReplyMarkup ReplyMarkup // RequestOpts are an additional optional field to configure timeouts for individual requests RequestOpts *RequestOpts @@ -4427,7 +4444,7 @@ type SendVideoNoteOpts struct { // SendVideoNote (https://core.telegram.org/bots/api#sendvideonote) // // As of v.4.0, Telegram clients support rounded square MPEG4 videos of up to 1 minute long. Use this method to send video messages. On success, the sent Message is returned. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - videoNote (type InputFile): Video note to send. Pass a file_id as String to send a video note that exists on the Telegram servers (recommended) or upload a new video using multipart/form-data. More information on Sending Files: https://core.telegram.org/bots/api#sending-files. Sending video notes by a URL is currently unsupported // - opts (type SendVideoNoteOpts): All optional parameters. func (bot *Bot) SendVideoNote(chatId int64, videoNote InputFile, opts *SendVideoNoteOpts) (*Message, error) { @@ -4539,7 +4556,7 @@ type SendVoiceOpts struct { ProtectContent bool // Description of the message to reply to ReplyParameters *ReplyParameters - // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user. Not supported for messages sent on behalf of a business account. + // Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user ReplyMarkup ReplyMarkup // RequestOpts are an additional optional field to configure timeouts for individual requests RequestOpts *RequestOpts @@ -4547,8 +4564,8 @@ type SendVoiceOpts struct { // SendVoice (https://core.telegram.org/bots/api#sendvoice) // -// Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. For this to work, your audio must be in an .OGG file encoded with OPUS (other formats may be sent as Audio or Document). On success, the sent Message is returned. Bots can currently send voice messages of up to 50 MB in size, this limit may be changed in the future. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. For this to work, your audio must be in an .OGG file encoded with OPUS, or in .MP3 format, or in .M4A format (other formats may be sent as Audio or Document). On success, the sent Message is returned. Bots can currently send voice messages of up to 50 MB in size, this limit may be changed in the future. +// - chatId (type int64): Unique identifier for the target chat // - voice (type InputFile): Audio file to send. Pass a file_id as String to send a file that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data. More information on Sending Files: https://core.telegram.org/bots/api#sending-files // - opts (type SendVoiceOpts): All optional parameters. func (bot *Bot) SendVoice(chatId int64, voice InputFile, opts *SendVoiceOpts) (*Message, error) { @@ -4634,7 +4651,7 @@ type SetChatAdministratorCustomTitleOpts struct { // SetChatAdministratorCustomTitle (https://core.telegram.org/bots/api#setchatadministratorcustomtitle) // // Use this method to set a custom title for an administrator in a supergroup promoted by the bot. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) +// - chatId (type int64): Unique identifier for the target chat // - userId (type int64): Unique identifier of the target user // - customTitle (type string): New custom title for the administrator; 0-16 characters, emoji are not allowed // - opts (type SetChatAdministratorCustomTitleOpts): All optional parameters. @@ -4669,7 +4686,7 @@ type SetChatDescriptionOpts struct { // SetChatDescription (https://core.telegram.org/bots/api#setchatdescription) // // Use this method to change the description of a group, a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - opts (type SetChatDescriptionOpts): All optional parameters. func (bot *Bot) SetChatDescription(chatId int64, opts *SetChatDescriptionOpts) (bool, error) { v := map[string]string{} @@ -4744,7 +4761,7 @@ type SetChatPermissionsOpts struct { // SetChatPermissions (https://core.telegram.org/bots/api#setchatpermissions) // // Use this method to set default chat permissions for all members. The bot must be an administrator in the group or a supergroup for this to work and must have the can_restrict_members administrator rights. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) +// - chatId (type int64): Unique identifier for the target chat // - permissions (type ChatPermissions): A JSON-serialized object for new default chat permissions // - opts (type SetChatPermissionsOpts): All optional parameters. func (bot *Bot) SetChatPermissions(chatId int64, permissions ChatPermissions, opts *SetChatPermissionsOpts) (bool, error) { @@ -4782,7 +4799,7 @@ type SetChatPhotoOpts struct { // SetChatPhoto (https://core.telegram.org/bots/api#setchatphoto) // // Use this method to set a new profile photo for the chat. Photos can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - photo (type InputFile): New chat photo, uploaded using multipart/form-data // - opts (type SetChatPhotoOpts): All optional parameters. func (bot *Bot) SetChatPhoto(chatId int64, photo InputFile, opts *SetChatPhotoOpts) (bool, error) { @@ -4831,7 +4848,7 @@ type SetChatStickerSetOpts struct { // SetChatStickerSet (https://core.telegram.org/bots/api#setchatstickerset) // // Use this method to set a new group sticker set for a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. Use the field can_set_sticker_set optionally returned in getChat requests to check if the bot can use this method. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) +// - chatId (type int64): Unique identifier for the target chat // - stickerSetName (type string): Name of the sticker set to be set as the group sticker set // - opts (type SetChatStickerSetOpts): All optional parameters. func (bot *Bot) SetChatStickerSet(chatId int64, stickerSetName string, opts *SetChatStickerSetOpts) (bool, error) { @@ -4862,7 +4879,7 @@ type SetChatTitleOpts struct { // SetChatTitle (https://core.telegram.org/bots/api#setchattitle) // // Use this method to change the title of a chat. Titles can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - title (type string): New chat title, 1-128 characters // - opts (type SetChatTitleOpts): All optional parameters. func (bot *Bot) SetChatTitle(chatId int64, title string, opts *SetChatTitleOpts) (bool, error) { @@ -4991,7 +5008,7 @@ type SetMessageReactionOpts struct { // SetMessageReaction (https://core.telegram.org/bots/api#setmessagereaction) // // Use this method to change the chosen reactions on a message. Service messages can't be reacted to. Automatically forwarded messages from a channel to its discussion group have the same available reactions as messages in the channel. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - messageId (type int64): Identifier of the target message. If the message belongs to a media group, the reaction is set to the first non-deleted message in the group instead. // - opts (type SetMessageReactionOpts): All optional parameters. func (bot *Bot) SetMessageReaction(chatId int64, messageId int64, opts *SetMessageReactionOpts) (bool, error) { @@ -5570,7 +5587,7 @@ func (bot *Bot) SetWebhook(url string, opts *SetWebhookOpts) (bool, error) { // StopMessageLiveLocationOpts is the set of optional fields for Bot.StopMessageLiveLocation. type StopMessageLiveLocationOpts struct { - // Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format @channelusername) + // Required if inline_message_id is not specified. Unique identifier for the target chat ChatId int64 // Required if inline_message_id is not specified. Identifier of the message with live location to stop MessageId int64 @@ -5636,7 +5653,7 @@ type StopPollOpts struct { // StopPoll (https://core.telegram.org/bots/api#stoppoll) // // Use this method to stop a poll which was sent by the bot. On success, the stopped Poll is returned. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - messageId (type int64): Identifier of the original message with the poll // - opts (type StopPollOpts): All optional parameters. func (bot *Bot) StopPoll(chatId int64, messageId int64, opts *StopPollOpts) (*Poll, error) { @@ -5676,7 +5693,7 @@ type UnbanChatMemberOpts struct { // UnbanChatMember (https://core.telegram.org/bots/api#unbanchatmember) // // Use this method to unban a previously banned user in a supergroup or channel. The user will not return to the group or channel automatically, but will be able to join via link, etc. The bot must be an administrator for this to work. By default, this method guarantees that after the call the user is not a member of the chat, but will be able to join it. So if the user is a member of the chat they will also be removed from the chat. If you don't want this, use the parameter only_if_banned. Returns True on success. -// - chatId (type int64): Unique identifier for the target group or username of the target supergroup or channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target group // - userId (type int64): Unique identifier of the target user // - opts (type UnbanChatMemberOpts): All optional parameters. func (bot *Bot) UnbanChatMember(chatId int64, userId int64, opts *UnbanChatMemberOpts) (bool, error) { @@ -5710,7 +5727,7 @@ type UnbanChatSenderChatOpts struct { // UnbanChatSenderChat (https://core.telegram.org/bots/api#unbanchatsenderchat) // // Use this method to unban a previously banned channel chat in a supergroup or channel. The bot must be an administrator for this to work and must have the appropriate administrator rights. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - senderChatId (type int64): Unique identifier of the target sender chat // - opts (type UnbanChatSenderChatOpts): All optional parameters. func (bot *Bot) UnbanChatSenderChat(chatId int64, senderChatId int64, opts *UnbanChatSenderChatOpts) (bool, error) { @@ -5741,7 +5758,7 @@ type UnhideGeneralForumTopicOpts struct { // UnhideGeneralForumTopic (https://core.telegram.org/bots/api#unhidegeneralforumtopic) // // Use this method to unhide the 'General' topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the can_manage_topics administrator rights. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) +// - chatId (type int64): Unique identifier for the target chat // - opts (type UnhideGeneralForumTopicOpts): All optional parameters. func (bot *Bot) UnhideGeneralForumTopic(chatId int64, opts *UnhideGeneralForumTopicOpts) (bool, error) { v := map[string]string{} @@ -5770,7 +5787,7 @@ type UnpinAllChatMessagesOpts struct { // UnpinAllChatMessages (https://core.telegram.org/bots/api#unpinallchatmessages) // // Use this method to clear the list of pinned messages in a chat. If the chat is not a private chat, the bot must be an administrator in the chat for this to work and must have the 'can_pin_messages' administrator right in a supergroup or 'can_edit_messages' administrator right in a channel. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - opts (type UnpinAllChatMessagesOpts): All optional parameters. func (bot *Bot) UnpinAllChatMessages(chatId int64, opts *UnpinAllChatMessagesOpts) (bool, error) { v := map[string]string{} @@ -5799,7 +5816,7 @@ type UnpinAllForumTopicMessagesOpts struct { // UnpinAllForumTopicMessages (https://core.telegram.org/bots/api#unpinallforumtopicmessages) // // Use this method to clear the list of pinned messages in a forum topic. The bot must be an administrator in the chat for this to work and must have the can_pin_messages administrator right in the supergroup. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) +// - chatId (type int64): Unique identifier for the target chat // - messageThreadId (type int64): Unique identifier for the target message thread of the forum topic // - opts (type UnpinAllForumTopicMessagesOpts): All optional parameters. func (bot *Bot) UnpinAllForumTopicMessages(chatId int64, messageThreadId int64, opts *UnpinAllForumTopicMessagesOpts) (bool, error) { @@ -5830,7 +5847,7 @@ type UnpinAllGeneralForumTopicMessagesOpts struct { // UnpinAllGeneralForumTopicMessages (https://core.telegram.org/bots/api#unpinallgeneralforumtopicmessages) // // Use this method to clear the list of pinned messages in a General forum topic. The bot must be an administrator in the chat for this to work and must have the can_pin_messages administrator right in the supergroup. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) +// - chatId (type int64): Unique identifier for the target chat // - opts (type UnpinAllGeneralForumTopicMessagesOpts): All optional parameters. func (bot *Bot) UnpinAllGeneralForumTopicMessages(chatId int64, opts *UnpinAllGeneralForumTopicMessagesOpts) (bool, error) { v := map[string]string{} @@ -5861,7 +5878,7 @@ type UnpinChatMessageOpts struct { // UnpinChatMessage (https://core.telegram.org/bots/api#unpinchatmessage) // // Use this method to remove a message from the list of pinned messages in a chat. If the chat is not a private chat, the bot must be an administrator in the chat for this to work and must have the 'can_pin_messages' administrator right in a supergroup or 'can_edit_messages' administrator right in a channel. Returns True on success. -// - chatId (type int64): Unique identifier for the target chat or username of the target channel (in the format @channelusername) +// - chatId (type int64): Unique identifier for the target chat // - opts (type UnpinChatMessageOpts): All optional parameters. func (bot *Bot) UnpinChatMessage(chatId int64, opts *UnpinChatMessageOpts) (bool, error) { v := map[string]string{} diff --git a/vendor/github.com/PaulSonOfLars/gotgbot/v2/gen_types.go b/vendor/github.com/PaulSonOfLars/gotgbot/v2/gen_types.go index db6004bb..098dd587 100644 --- a/vendor/github.com/PaulSonOfLars/gotgbot/v2/gen_types.go +++ b/vendor/github.com/PaulSonOfLars/gotgbot/v2/gen_types.go @@ -70,7 +70,602 @@ type Audio struct { Thumbnail *PhotoSize `json:"thumbnail,omitempty"` } +// BackgroundFill (https://core.telegram.org/bots/api#backgroundfill) +// +// This object describes the way a background is filled based on the selected colors. Currently, it can be one of +// - BackgroundFillSolid +// - BackgroundFillGradient +// - BackgroundFillFreeformGradient +type BackgroundFill interface { + GetType() string + // MergeBackgroundFill returns a MergedBackgroundFill struct to simplify working with complex telegram types in a non-generic world. + MergeBackgroundFill() MergedBackgroundFill + // backgroundFill exists to avoid external types implementing this interface. + backgroundFill() +} + +// Ensure that all subtypes correctly implement the parent interface. +var ( + _ BackgroundFill = BackgroundFillSolid{} + _ BackgroundFill = BackgroundFillGradient{} + _ BackgroundFill = BackgroundFillFreeformGradient{} +) + +// MergedBackgroundFill is a helper type to simplify interactions with the various BackgroundFill subtypes. +type MergedBackgroundFill struct { + // Type of the background fill + Type string `json:"type"` + // Optional. The color of the background fill in the RGB24 format (Only for solid) + Color int64 `json:"color,omitempty"` + // Optional. Top color of the gradient in the RGB24 format (Only for gradient) + TopColor int64 `json:"top_color,omitempty"` + // Optional. Bottom color of the gradient in the RGB24 format (Only for gradient) + BottomColor int64 `json:"bottom_color,omitempty"` + // Optional. Clockwise rotation angle of the background fill in degrees; 0-359 (Only for gradient) + RotationAngle int64 `json:"rotation_angle,omitempty"` + // Optional. A list of the 3 or 4 base colors that are used to generate the freeform gradient in the RGB24 format (Only for freeform_gradient) + Colors []int64 `json:"colors,omitempty"` +} + +// GetType is a helper method to easily access the common fields of an interface. +func (v MergedBackgroundFill) GetType() string { + return v.Type +} + +// MergedBackgroundFill.backgroundFill is a dummy method to avoid interface implementation. +func (v MergedBackgroundFill) backgroundFill() {} + +// MergeBackgroundFill returns a MergedBackgroundFill struct to simplify working with types in a non-generic world. +func (v MergedBackgroundFill) MergeBackgroundFill() MergedBackgroundFill { + return v +} + +// unmarshalBackgroundFillArray is a JSON unmarshalling helper which allows unmarshalling an array of interfaces +// using unmarshalBackgroundFill. +func unmarshalBackgroundFillArray(d json.RawMessage) ([]BackgroundFill, error) { + if len(d) == 0 { + return nil, nil + } + + var ds []json.RawMessage + err := json.Unmarshal(d, &ds) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal initial BackgroundFill JSON into an array: %w", err) + } + + var vs []BackgroundFill + for idx, d := range ds { + v, err := unmarshalBackgroundFill(d) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal BackgroundFill on array item %d: %w", idx, err) + } + vs = append(vs, v) + } + + return vs, nil +} + +// unmarshalBackgroundFill is a JSON unmarshal helper to marshal the right structs into a BackgroundFill interface +// based on the Type field. +func unmarshalBackgroundFill(d json.RawMessage) (BackgroundFill, error) { + if len(d) == 0 { + return nil, nil + } + + t := struct { + Type string + }{} + err := json.Unmarshal(d, &t) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal BackgroundFill for constant field 'Type': %w", err) + } + + switch t.Type { + case "solid": + s := BackgroundFillSolid{} + err := json.Unmarshal(d, &s) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal BackgroundFill for value 'solid': %w", err) + } + return s, nil + + case "gradient": + s := BackgroundFillGradient{} + err := json.Unmarshal(d, &s) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal BackgroundFill for value 'gradient': %w", err) + } + return s, nil + + case "freeform_gradient": + s := BackgroundFillFreeformGradient{} + err := json.Unmarshal(d, &s) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal BackgroundFill for value 'freeform_gradient': %w", err) + } + return s, nil + + } + return nil, fmt.Errorf("unknown interface for BackgroundFill with Type %v", t.Type) +} + +// BackgroundFillFreeformGradient (https://core.telegram.org/bots/api#backgroundfillfreeformgradient) +// +// The background is a freeform gradient that rotates after every message in the chat. +type BackgroundFillFreeformGradient struct { + // A list of the 3 or 4 base colors that are used to generate the freeform gradient in the RGB24 format + Colors []int64 `json:"colors,omitempty"` +} + +// GetType is a helper method to easily access the common fields of an interface. +func (v BackgroundFillFreeformGradient) GetType() string { + return "freeform_gradient" +} + +// MergeBackgroundFill returns a MergedBackgroundFill struct to simplify working with types in a non-generic world. +func (v BackgroundFillFreeformGradient) MergeBackgroundFill() MergedBackgroundFill { + return MergedBackgroundFill{ + Type: "freeform_gradient", + Colors: v.Colors, + } +} + +// MarshalJSON is a custom JSON marshaller to allow for enforcing the Type value. +func (v BackgroundFillFreeformGradient) MarshalJSON() ([]byte, error) { + type alias BackgroundFillFreeformGradient + a := struct { + Type string `json:"type"` + alias + }{ + Type: "freeform_gradient", + alias: (alias)(v), + } + return json.Marshal(a) +} + +// BackgroundFillFreeformGradient.backgroundFill is a dummy method to avoid interface implementation. +func (v BackgroundFillFreeformGradient) backgroundFill() {} + +// BackgroundFillGradient (https://core.telegram.org/bots/api#backgroundfillgradient) +// +// The background is a gradient fill. +type BackgroundFillGradient struct { + // Top color of the gradient in the RGB24 format + TopColor int64 `json:"top_color"` + // Bottom color of the gradient in the RGB24 format + BottomColor int64 `json:"bottom_color"` + // Clockwise rotation angle of the background fill in degrees; 0-359 + RotationAngle int64 `json:"rotation_angle"` +} + +// GetType is a helper method to easily access the common fields of an interface. +func (v BackgroundFillGradient) GetType() string { + return "gradient" +} + +// MergeBackgroundFill returns a MergedBackgroundFill struct to simplify working with types in a non-generic world. +func (v BackgroundFillGradient) MergeBackgroundFill() MergedBackgroundFill { + return MergedBackgroundFill{ + Type: "gradient", + TopColor: v.TopColor, + BottomColor: v.BottomColor, + RotationAngle: v.RotationAngle, + } +} + +// MarshalJSON is a custom JSON marshaller to allow for enforcing the Type value. +func (v BackgroundFillGradient) MarshalJSON() ([]byte, error) { + type alias BackgroundFillGradient + a := struct { + Type string `json:"type"` + alias + }{ + Type: "gradient", + alias: (alias)(v), + } + return json.Marshal(a) +} + +// BackgroundFillGradient.backgroundFill is a dummy method to avoid interface implementation. +func (v BackgroundFillGradient) backgroundFill() {} + +// BackgroundFillSolid (https://core.telegram.org/bots/api#backgroundfillsolid) +// +// The background is filled using the selected color. +type BackgroundFillSolid struct { + // The color of the background fill in the RGB24 format + Color int64 `json:"color"` +} + +// GetType is a helper method to easily access the common fields of an interface. +func (v BackgroundFillSolid) GetType() string { + return "solid" +} + +// MergeBackgroundFill returns a MergedBackgroundFill struct to simplify working with types in a non-generic world. +func (v BackgroundFillSolid) MergeBackgroundFill() MergedBackgroundFill { + return MergedBackgroundFill{ + Type: "solid", + Color: v.Color, + } +} + +// MarshalJSON is a custom JSON marshaller to allow for enforcing the Type value. +func (v BackgroundFillSolid) MarshalJSON() ([]byte, error) { + type alias BackgroundFillSolid + a := struct { + Type string `json:"type"` + alias + }{ + Type: "solid", + alias: (alias)(v), + } + return json.Marshal(a) +} + +// BackgroundFillSolid.backgroundFill is a dummy method to avoid interface implementation. +func (v BackgroundFillSolid) backgroundFill() {} + +// BackgroundType (https://core.telegram.org/bots/api#backgroundtype) +// +// This object describes the type of a background. Currently, it can be one of +// - BackgroundTypeFill +// - BackgroundTypeWallpaper +// - BackgroundTypePattern +// - BackgroundTypeChatTheme +type BackgroundType interface { + GetType() string + // MergeBackgroundType returns a MergedBackgroundType struct to simplify working with complex telegram types in a non-generic world. + MergeBackgroundType() MergedBackgroundType + // backgroundType exists to avoid external types implementing this interface. + backgroundType() +} + +// Ensure that all subtypes correctly implement the parent interface. +var ( + _ BackgroundType = BackgroundTypeFill{} + _ BackgroundType = BackgroundTypeWallpaper{} + _ BackgroundType = BackgroundTypePattern{} + _ BackgroundType = BackgroundTypeChatTheme{} +) + +// MergedBackgroundType is a helper type to simplify interactions with the various BackgroundType subtypes. +type MergedBackgroundType struct { + // Type of the background + Type string `json:"type"` + // Optional. The background fill (Only for fill, pattern) + Fill BackgroundFill `json:"fill,omitempty"` + // Optional. Dimming of the background in dark themes, as a percentage; 0-100 (Only for fill, wallpaper) + DarkThemeDimming int64 `json:"dark_theme_dimming,omitempty"` + // Optional. Document with the wallpaper (Only for wallpaper, pattern) + Document *Document `json:"document,omitempty"` + // Optional. True, if the wallpaper is downscaled to fit in a 450x450 square and then box-blurred with radius 12 (Only for wallpaper) + IsBlurred bool `json:"is_blurred,omitempty"` + // Optional. True, if the background moves slightly when the device is tilted (Only for wallpaper, pattern) + IsMoving bool `json:"is_moving,omitempty"` + // Optional. Intensity of the pattern when it is shown above the filled background; 0-100 (Only for pattern) + Intensity int64 `json:"intensity,omitempty"` + // Optional. True, if the background fill must be applied only to the pattern itself. All other pixels are black in this case. For dark themes only (Only for pattern) + IsInverted bool `json:"is_inverted,omitempty"` + // Optional. Name of the chat theme, which is usually an emoji (Only for chat_theme) + ThemeName string `json:"theme_name,omitempty"` +} + +// GetType is a helper method to easily access the common fields of an interface. +func (v MergedBackgroundType) GetType() string { + return v.Type +} + +// MergedBackgroundType.backgroundType is a dummy method to avoid interface implementation. +func (v MergedBackgroundType) backgroundType() {} + +// MergeBackgroundType returns a MergedBackgroundType struct to simplify working with types in a non-generic world. +func (v MergedBackgroundType) MergeBackgroundType() MergedBackgroundType { + return v +} + +// unmarshalBackgroundTypeArray is a JSON unmarshalling helper which allows unmarshalling an array of interfaces +// using unmarshalBackgroundType. +func unmarshalBackgroundTypeArray(d json.RawMessage) ([]BackgroundType, error) { + if len(d) == 0 { + return nil, nil + } + + var ds []json.RawMessage + err := json.Unmarshal(d, &ds) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal initial BackgroundType JSON into an array: %w", err) + } + + var vs []BackgroundType + for idx, d := range ds { + v, err := unmarshalBackgroundType(d) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal BackgroundType on array item %d: %w", idx, err) + } + vs = append(vs, v) + } + + return vs, nil +} + +// unmarshalBackgroundType is a JSON unmarshal helper to marshal the right structs into a BackgroundType interface +// based on the Type field. +func unmarshalBackgroundType(d json.RawMessage) (BackgroundType, error) { + if len(d) == 0 { + return nil, nil + } + + t := struct { + Type string + }{} + err := json.Unmarshal(d, &t) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal BackgroundType for constant field 'Type': %w", err) + } + + switch t.Type { + case "fill": + s := BackgroundTypeFill{} + err := json.Unmarshal(d, &s) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal BackgroundType for value 'fill': %w", err) + } + return s, nil + + case "wallpaper": + s := BackgroundTypeWallpaper{} + err := json.Unmarshal(d, &s) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal BackgroundType for value 'wallpaper': %w", err) + } + return s, nil + + case "pattern": + s := BackgroundTypePattern{} + err := json.Unmarshal(d, &s) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal BackgroundType for value 'pattern': %w", err) + } + return s, nil + + case "chat_theme": + s := BackgroundTypeChatTheme{} + err := json.Unmarshal(d, &s) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal BackgroundType for value 'chat_theme': %w", err) + } + return s, nil + + } + return nil, fmt.Errorf("unknown interface for BackgroundType with Type %v", t.Type) +} + +// BackgroundTypeChatTheme (https://core.telegram.org/bots/api#backgroundtypechattheme) +// +// The background is taken directly from a built-in chat theme. +type BackgroundTypeChatTheme struct { + // Name of the chat theme, which is usually an emoji + ThemeName string `json:"theme_name"` +} + +// GetType is a helper method to easily access the common fields of an interface. +func (v BackgroundTypeChatTheme) GetType() string { + return "chat_theme" +} + +// MergeBackgroundType returns a MergedBackgroundType struct to simplify working with types in a non-generic world. +func (v BackgroundTypeChatTheme) MergeBackgroundType() MergedBackgroundType { + return MergedBackgroundType{ + Type: "chat_theme", + ThemeName: v.ThemeName, + } +} + +// MarshalJSON is a custom JSON marshaller to allow for enforcing the Type value. +func (v BackgroundTypeChatTheme) MarshalJSON() ([]byte, error) { + type alias BackgroundTypeChatTheme + a := struct { + Type string `json:"type"` + alias + }{ + Type: "chat_theme", + alias: (alias)(v), + } + return json.Marshal(a) +} + +// BackgroundTypeChatTheme.backgroundType is a dummy method to avoid interface implementation. +func (v BackgroundTypeChatTheme) backgroundType() {} + +// BackgroundTypeFill (https://core.telegram.org/bots/api#backgroundtypefill) +// +// The background is automatically filled based on the selected colors. +type BackgroundTypeFill struct { + // The background fill + Fill BackgroundFill `json:"fill"` + // Dimming of the background in dark themes, as a percentage; 0-100 + DarkThemeDimming int64 `json:"dark_theme_dimming"` +} + +// UnmarshalJSON is a custom JSON unmarshaller to use the helpers which allow for unmarshalling structs into interfaces. +func (v *BackgroundTypeFill) UnmarshalJSON(b []byte) error { + // All fields in BackgroundTypeFill, with interface fields as json.RawMessage + type tmp struct { + Fill json.RawMessage `json:"fill"` + DarkThemeDimming int64 `json:"dark_theme_dimming"` + } + t := tmp{} + err := json.Unmarshal(b, &t) + if err != nil { + return fmt.Errorf("failed to unmarshal BackgroundTypeFill JSON into tmp struct: %w", err) + } + + v.Fill, err = unmarshalBackgroundFill(t.Fill) + if err != nil { + return fmt.Errorf("failed to unmarshal custom JSON field Fill: %w", err) + } + v.DarkThemeDimming = t.DarkThemeDimming + + return nil +} + +// GetType is a helper method to easily access the common fields of an interface. +func (v BackgroundTypeFill) GetType() string { + return "fill" +} + +// MergeBackgroundType returns a MergedBackgroundType struct to simplify working with types in a non-generic world. +func (v BackgroundTypeFill) MergeBackgroundType() MergedBackgroundType { + return MergedBackgroundType{ + Type: "fill", + Fill: v.Fill, + DarkThemeDimming: v.DarkThemeDimming, + } +} + +// MarshalJSON is a custom JSON marshaller to allow for enforcing the Type value. +func (v BackgroundTypeFill) MarshalJSON() ([]byte, error) { + type alias BackgroundTypeFill + a := struct { + Type string `json:"type"` + alias + }{ + Type: "fill", + alias: (alias)(v), + } + return json.Marshal(a) +} + +// BackgroundTypeFill.backgroundType is a dummy method to avoid interface implementation. +func (v BackgroundTypeFill) backgroundType() {} + +// BackgroundTypePattern (https://core.telegram.org/bots/api#backgroundtypepattern) +// +// The background is a PNG or TGV (gzipped subset of SVG with MIME type "application/x-tgwallpattern") pattern to be combined with the background fill chosen by the user. +type BackgroundTypePattern struct { + // Document with the pattern + Document Document `json:"document"` + // The background fill that is combined with the pattern + Fill BackgroundFill `json:"fill"` + // Intensity of the pattern when it is shown above the filled background; 0-100 + Intensity int64 `json:"intensity"` + // Optional. True, if the background fill must be applied only to the pattern itself. All other pixels are black in this case. For dark themes only + IsInverted bool `json:"is_inverted,omitempty"` + // Optional. True, if the background moves slightly when the device is tilted + IsMoving bool `json:"is_moving,omitempty"` +} + +// UnmarshalJSON is a custom JSON unmarshaller to use the helpers which allow for unmarshalling structs into interfaces. +func (v *BackgroundTypePattern) UnmarshalJSON(b []byte) error { + // All fields in BackgroundTypePattern, with interface fields as json.RawMessage + type tmp struct { + Document Document `json:"document"` + Fill json.RawMessage `json:"fill"` + Intensity int64 `json:"intensity"` + IsInverted bool `json:"is_inverted"` + IsMoving bool `json:"is_moving"` + } + t := tmp{} + err := json.Unmarshal(b, &t) + if err != nil { + return fmt.Errorf("failed to unmarshal BackgroundTypePattern JSON into tmp struct: %w", err) + } + + v.Document = t.Document + v.Fill, err = unmarshalBackgroundFill(t.Fill) + if err != nil { + return fmt.Errorf("failed to unmarshal custom JSON field Fill: %w", err) + } + v.Intensity = t.Intensity + v.IsInverted = t.IsInverted + v.IsMoving = t.IsMoving + + return nil +} + +// GetType is a helper method to easily access the common fields of an interface. +func (v BackgroundTypePattern) GetType() string { + return "pattern" +} + +// MergeBackgroundType returns a MergedBackgroundType struct to simplify working with types in a non-generic world. +func (v BackgroundTypePattern) MergeBackgroundType() MergedBackgroundType { + return MergedBackgroundType{ + Type: "pattern", + Document: &v.Document, + Fill: v.Fill, + Intensity: v.Intensity, + IsInverted: v.IsInverted, + IsMoving: v.IsMoving, + } +} + +// MarshalJSON is a custom JSON marshaller to allow for enforcing the Type value. +func (v BackgroundTypePattern) MarshalJSON() ([]byte, error) { + type alias BackgroundTypePattern + a := struct { + Type string `json:"type"` + alias + }{ + Type: "pattern", + alias: (alias)(v), + } + return json.Marshal(a) +} + +// BackgroundTypePattern.backgroundType is a dummy method to avoid interface implementation. +func (v BackgroundTypePattern) backgroundType() {} + +// BackgroundTypeWallpaper (https://core.telegram.org/bots/api#backgroundtypewallpaper) +// +// The background is a wallpaper in the JPEG format. +type BackgroundTypeWallpaper struct { + // Document with the wallpaper + Document Document `json:"document"` + // Dimming of the background in dark themes, as a percentage; 0-100 + DarkThemeDimming int64 `json:"dark_theme_dimming"` + // Optional. True, if the wallpaper is downscaled to fit in a 450x450 square and then box-blurred with radius 12 + IsBlurred bool `json:"is_blurred,omitempty"` + // Optional. True, if the background moves slightly when the device is tilted + IsMoving bool `json:"is_moving,omitempty"` +} + +// GetType is a helper method to easily access the common fields of an interface. +func (v BackgroundTypeWallpaper) GetType() string { + return "wallpaper" +} + +// MergeBackgroundType returns a MergedBackgroundType struct to simplify working with types in a non-generic world. +func (v BackgroundTypeWallpaper) MergeBackgroundType() MergedBackgroundType { + return MergedBackgroundType{ + Type: "wallpaper", + Document: &v.Document, + DarkThemeDimming: v.DarkThemeDimming, + IsBlurred: v.IsBlurred, + IsMoving: v.IsMoving, + } +} + +// MarshalJSON is a custom JSON marshaller to allow for enforcing the Type value. +func (v BackgroundTypeWallpaper) MarshalJSON() ([]byte, error) { + type alias BackgroundTypeWallpaper + a := struct { + Type string `json:"type"` + alias + }{ + Type: "wallpaper", + alias: (alias)(v), + } + return json.Marshal(a) +} + +// BackgroundTypeWallpaper.backgroundType is a dummy method to avoid interface implementation. +func (v BackgroundTypeWallpaper) backgroundType() {} + // Birthdate (https://core.telegram.org/bots/api#birthdate) +// +// Describes the birthdate of a user. type Birthdate struct { // Day of the user's birth; 1-31 Day int64 `json:"day"` @@ -123,7 +718,7 @@ var ( type MergedBotCommandScope struct { // Scope type Type string `json:"type"` - // Optional. Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) (Only for chat, chat_administrators, chat_member) + // Optional. Unique identifier for the target chat (Only for chat, chat_administrators, chat_member) ChatId int64 `json:"chat_id,omitempty"` // Optional. Unique identifier of the target user (Only for chat_member) UserId int64 `json:"user_id,omitempty"` @@ -245,7 +840,7 @@ func (v BotCommandScopeAllPrivateChats) botCommandScope() {} // // Represents the scope of bot commands, covering a specific chat. type BotCommandScopeChat struct { - // Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) + // Unique identifier for the target chat ChatId int64 `json:"chat_id"` } @@ -282,7 +877,7 @@ func (v BotCommandScopeChat) botCommandScope() {} // // Represents the scope of bot commands, covering all administrators of a specific group or supergroup chat. type BotCommandScopeChatAdministrators struct { - // Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) + // Unique identifier for the target chat ChatId int64 `json:"chat_id"` } @@ -319,7 +914,7 @@ func (v BotCommandScopeChatAdministrators) botCommandScope() {} // // Represents the scope of bot commands, covering a specific member of a group or supergroup chat. type BotCommandScopeChatMember struct { - // Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) + // Unique identifier for the target chat ChatId int64 `json:"chat_id"` // Unique identifier of the target user UserId int64 `json:"user_id"` @@ -431,6 +1026,8 @@ type BusinessConnection struct { } // BusinessIntro (https://core.telegram.org/bots/api#businessintro) +// +// Contains information about the start page settings of a Telegram Business account. type BusinessIntro struct { // Optional. Title text of the business intro Title string `json:"title,omitempty"` @@ -441,6 +1038,8 @@ type BusinessIntro struct { } // BusinessLocation (https://core.telegram.org/bots/api#businesslocation) +// +// Contains information about the location of a Telegram Business account. type BusinessLocation struct { // Address of the business Address string `json:"address"` @@ -461,6 +1060,8 @@ type BusinessMessagesDeleted struct { } // BusinessOpeningHours (https://core.telegram.org/bots/api#businessopeninghours) +// +// Describes the opening hours of a business. type BusinessOpeningHours struct { // Unique name of the time zone for which the opening hours are defined TimeZoneName string `json:"time_zone_name"` @@ -469,6 +1070,8 @@ type BusinessOpeningHours struct { } // BusinessOpeningHoursInterval (https://core.telegram.org/bots/api#businessopeninghoursinterval) +// +// Describes an interval of time during which a business is open. type BusinessOpeningHoursInterval struct { // The minute's sequence number in a week, starting on Monday, marking the start of the time interval during which the business is open; 0 - 7 * 24 * 60 OpeningMinute int64 `json:"opening_minute"` @@ -499,232 +1102,60 @@ type CallbackQuery struct { Data string `json:"data,omitempty"` // Optional. Short name of a Game to be returned, serves as the unique identifier for the game GameShortName string `json:"game_short_name,omitempty"` -} - -// UnmarshalJSON is a custom JSON unmarshaller to use the helpers which allow for unmarshalling structs into interfaces. -func (v *CallbackQuery) UnmarshalJSON(b []byte) error { - // All fields in CallbackQuery, with interface fields as json.RawMessage - type tmp struct { - Id string `json:"id"` - From User `json:"from"` - Message json.RawMessage `json:"message"` - InlineMessageId string `json:"inline_message_id"` - ChatInstance string `json:"chat_instance"` - Data string `json:"data"` - GameShortName string `json:"game_short_name"` - } - t := tmp{} - err := json.Unmarshal(b, &t) - if err != nil { - return fmt.Errorf("failed to unmarshal CallbackQuery JSON into tmp struct: %w", err) - } - - v.Id = t.Id - v.From = t.From - v.Message, err = unmarshalMaybeInaccessibleMessage(t.Message) - if err != nil { - return fmt.Errorf("failed to unmarshal custom JSON field Message: %w", err) - } - v.InlineMessageId = t.InlineMessageId - v.ChatInstance = t.ChatInstance - v.Data = t.Data - v.GameShortName = t.GameShortName - - return nil -} - -// Chat (https://core.telegram.org/bots/api#chat) -// -// This object represents a chat. -type Chat struct { - // Unique identifier for this chat. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this identifier. - Id int64 `json:"id"` - // Type of chat, can be either "private", "group", "supergroup" or "channel" - Type string `json:"type"` - // Optional. Title, for supergroups, channels and group chats - Title string `json:"title,omitempty"` - // Optional. Username, for private chats, supergroups and channels if available - Username string `json:"username,omitempty"` - // Optional. First name of the other party in a private chat - FirstName string `json:"first_name,omitempty"` - // Optional. Last name of the other party in a private chat - LastName string `json:"last_name,omitempty"` - // Optional. True, if the supergroup chat is a forum (has topics enabled) - IsForum bool `json:"is_forum,omitempty"` - // Optional. Chat photo. Returned only in getChat. - Photo *ChatPhoto `json:"photo,omitempty"` - // Optional. If non-empty, the list of all active chat usernames; for private chats, supergroups and channels. Returned only in getChat. - ActiveUsernames []string `json:"active_usernames,omitempty"` - // Optional. For private chats, the date of birth of the user. Returned only in getChat. - Birthdate *Birthdate `json:"birthdate,omitempty"` - // Optional. For private chats with business accounts, the intro of the business. Returned only in getChat. - BusinessIntro *BusinessIntro `json:"business_intro,omitempty"` - // Optional. For private chats with business accounts, the location of the business. Returned only in getChat. - BusinessLocation *BusinessLocation `json:"business_location,omitempty"` - // Optional. For private chats with business accounts, the opening hours of the business. Returned only in getChat. - BusinessOpeningHours *BusinessOpeningHours `json:"business_opening_hours,omitempty"` - // Optional. For private chats, the personal channel of the user. Returned only in getChat. - PersonalChat *Chat `json:"personal_chat,omitempty"` - // Optional. List of available reactions allowed in the chat. If omitted, then all emoji reactions are allowed. Returned only in getChat. - AvailableReactions []ReactionType `json:"available_reactions,omitempty"` - // Optional. Identifier of the accent color for the chat name and backgrounds of the chat photo, reply header, and link preview. See accent colors for more details. Returned only in getChat. Always returned in getChat. - AccentColorId int64 `json:"accent_color_id,omitempty"` - // Optional. Custom emoji identifier of emoji chosen by the chat for the reply header and link preview background. Returned only in getChat. - BackgroundCustomEmojiId string `json:"background_custom_emoji_id,omitempty"` - // Optional. Identifier of the accent color for the chat's profile background. See profile accent colors for more details. Returned only in getChat. - ProfileAccentColorId int64 `json:"profile_accent_color_id,omitempty"` - // Optional. Custom emoji identifier of the emoji chosen by the chat for its profile background. Returned only in getChat. - ProfileBackgroundCustomEmojiId string `json:"profile_background_custom_emoji_id,omitempty"` - // Optional. Custom emoji identifier of the emoji status of the chat or the other party in a private chat. Returned only in getChat. - EmojiStatusCustomEmojiId string `json:"emoji_status_custom_emoji_id,omitempty"` - // Optional. Expiration date of the emoji status of the chat or the other party in a private chat, in Unix time, if any. Returned only in getChat. - EmojiStatusExpirationDate int64 `json:"emoji_status_expiration_date,omitempty"` - // Optional. Bio of the other party in a private chat. Returned only in getChat. - Bio string `json:"bio,omitempty"` - // Optional. True, if privacy settings of the other party in the private chat allows to use tg://user?id= links only in chats with the user. Returned only in getChat. - HasPrivateForwards bool `json:"has_private_forwards,omitempty"` - // Optional. True, if the privacy settings of the other party restrict sending voice and video note messages in the private chat. Returned only in getChat. - HasRestrictedVoiceAndVideoMessages bool `json:"has_restricted_voice_and_video_messages,omitempty"` - // Optional. True, if users need to join the supergroup before they can send messages. Returned only in getChat. - JoinToSendMessages bool `json:"join_to_send_messages,omitempty"` - // Optional. True, if all users directly joining the supergroup need to be approved by supergroup administrators. Returned only in getChat. - JoinByRequest bool `json:"join_by_request,omitempty"` - // Optional. Description, for groups, supergroups and channel chats. Returned only in getChat. - Description string `json:"description,omitempty"` - // Optional. Primary invite link, for groups, supergroups and channel chats. Returned only in getChat. - InviteLink string `json:"invite_link,omitempty"` - // Optional. The most recent pinned message (by sending date). Returned only in getChat. - PinnedMessage *Message `json:"pinned_message,omitempty"` - // Optional. Default chat member permissions, for groups and supergroups. Returned only in getChat. - Permissions *ChatPermissions `json:"permissions,omitempty"` - // Optional. For supergroups, the minimum allowed delay between consecutive messages sent by each unprivileged user; in seconds. Returned only in getChat. - SlowModeDelay int64 `json:"slow_mode_delay,omitempty"` - // Optional. For supergroups, the minimum number of boosts that a non-administrator user needs to add in order to ignore slow mode and chat permissions. Returned only in getChat. - UnrestrictBoostCount int64 `json:"unrestrict_boost_count,omitempty"` - // Optional. The time after which all messages sent to the chat will be automatically deleted; in seconds. Returned only in getChat. - MessageAutoDeleteTime int64 `json:"message_auto_delete_time,omitempty"` - // Optional. True, if aggressive anti-spam checks are enabled in the supergroup. The field is only available to chat administrators. Returned only in getChat. - HasAggressiveAntiSpamEnabled bool `json:"has_aggressive_anti_spam_enabled,omitempty"` - // Optional. True, if non-administrators can only get the list of bots and administrators in the chat. Returned only in getChat. - HasHiddenMembers bool `json:"has_hidden_members,omitempty"` - // Optional. True, if messages from the chat can't be forwarded to other chats. Returned only in getChat. - HasProtectedContent bool `json:"has_protected_content,omitempty"` - // Optional. True, if new chat members will have access to old messages; available only to chat administrators. Returned only in getChat. - HasVisibleHistory bool `json:"has_visible_history,omitempty"` - // Optional. For supergroups, name of group sticker set. Returned only in getChat. - StickerSetName string `json:"sticker_set_name,omitempty"` - // Optional. True, if the bot can change the group sticker set. Returned only in getChat. - CanSetStickerSet bool `json:"can_set_sticker_set,omitempty"` - // Optional. For supergroups, the name of the group's custom emoji sticker set. Custom emoji from this set can be used by all users and bots in the group. Returned only in getChat. - CustomEmojiStickerSetName string `json:"custom_emoji_sticker_set_name,omitempty"` - // Optional. Unique identifier for the linked chat, i.e. the discussion group identifier for a channel and vice versa; for supergroups and channel chats. This identifier may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier. Returned only in getChat. - LinkedChatId int64 `json:"linked_chat_id,omitempty"` - // Optional. For supergroups, the location to which the supergroup is connected. Returned only in getChat. - Location *ChatLocation `json:"location,omitempty"` -} - -// UnmarshalJSON is a custom JSON unmarshaller to use the helpers which allow for unmarshalling structs into interfaces. -func (v *Chat) UnmarshalJSON(b []byte) error { - // All fields in Chat, with interface fields as json.RawMessage - type tmp struct { - Id int64 `json:"id"` - Type string `json:"type"` - Title string `json:"title"` - Username string `json:"username"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - IsForum bool `json:"is_forum"` - Photo *ChatPhoto `json:"photo"` - ActiveUsernames []string `json:"active_usernames"` - Birthdate *Birthdate `json:"birthdate"` - BusinessIntro *BusinessIntro `json:"business_intro"` - BusinessLocation *BusinessLocation `json:"business_location"` - BusinessOpeningHours *BusinessOpeningHours `json:"business_opening_hours"` - PersonalChat *Chat `json:"personal_chat"` - AvailableReactions json.RawMessage `json:"available_reactions"` - AccentColorId int64 `json:"accent_color_id"` - BackgroundCustomEmojiId string `json:"background_custom_emoji_id"` - ProfileAccentColorId int64 `json:"profile_accent_color_id"` - ProfileBackgroundCustomEmojiId string `json:"profile_background_custom_emoji_id"` - EmojiStatusCustomEmojiId string `json:"emoji_status_custom_emoji_id"` - EmojiStatusExpirationDate int64 `json:"emoji_status_expiration_date"` - Bio string `json:"bio"` - HasPrivateForwards bool `json:"has_private_forwards"` - HasRestrictedVoiceAndVideoMessages bool `json:"has_restricted_voice_and_video_messages"` - JoinToSendMessages bool `json:"join_to_send_messages"` - JoinByRequest bool `json:"join_by_request"` - Description string `json:"description"` - InviteLink string `json:"invite_link"` - PinnedMessage *Message `json:"pinned_message"` - Permissions *ChatPermissions `json:"permissions"` - SlowModeDelay int64 `json:"slow_mode_delay"` - UnrestrictBoostCount int64 `json:"unrestrict_boost_count"` - MessageAutoDeleteTime int64 `json:"message_auto_delete_time"` - HasAggressiveAntiSpamEnabled bool `json:"has_aggressive_anti_spam_enabled"` - HasHiddenMembers bool `json:"has_hidden_members"` - HasProtectedContent bool `json:"has_protected_content"` - HasVisibleHistory bool `json:"has_visible_history"` - StickerSetName string `json:"sticker_set_name"` - CanSetStickerSet bool `json:"can_set_sticker_set"` - CustomEmojiStickerSetName string `json:"custom_emoji_sticker_set_name"` - LinkedChatId int64 `json:"linked_chat_id"` - Location *ChatLocation `json:"location"` +} + +// UnmarshalJSON is a custom JSON unmarshaller to use the helpers which allow for unmarshalling structs into interfaces. +func (v *CallbackQuery) UnmarshalJSON(b []byte) error { + // All fields in CallbackQuery, with interface fields as json.RawMessage + type tmp struct { + Id string `json:"id"` + From User `json:"from"` + Message json.RawMessage `json:"message"` + InlineMessageId string `json:"inline_message_id"` + ChatInstance string `json:"chat_instance"` + Data string `json:"data"` + GameShortName string `json:"game_short_name"` } t := tmp{} err := json.Unmarshal(b, &t) if err != nil { - return fmt.Errorf("failed to unmarshal Chat JSON into tmp struct: %w", err) + return fmt.Errorf("failed to unmarshal CallbackQuery JSON into tmp struct: %w", err) } v.Id = t.Id - v.Type = t.Type - v.Title = t.Title - v.Username = t.Username - v.FirstName = t.FirstName - v.LastName = t.LastName - v.IsForum = t.IsForum - v.Photo = t.Photo - v.ActiveUsernames = t.ActiveUsernames - v.Birthdate = t.Birthdate - v.BusinessIntro = t.BusinessIntro - v.BusinessLocation = t.BusinessLocation - v.BusinessOpeningHours = t.BusinessOpeningHours - v.PersonalChat = t.PersonalChat - v.AvailableReactions, err = unmarshalReactionTypeArray(t.AvailableReactions) + v.From = t.From + v.Message, err = unmarshalMaybeInaccessibleMessage(t.Message) if err != nil { - return fmt.Errorf("failed to unmarshal custom JSON field AvailableReactions: %w", err) + return fmt.Errorf("failed to unmarshal custom JSON field Message: %w", err) } - v.AccentColorId = t.AccentColorId - v.BackgroundCustomEmojiId = t.BackgroundCustomEmojiId - v.ProfileAccentColorId = t.ProfileAccentColorId - v.ProfileBackgroundCustomEmojiId = t.ProfileBackgroundCustomEmojiId - v.EmojiStatusCustomEmojiId = t.EmojiStatusCustomEmojiId - v.EmojiStatusExpirationDate = t.EmojiStatusExpirationDate - v.Bio = t.Bio - v.HasPrivateForwards = t.HasPrivateForwards - v.HasRestrictedVoiceAndVideoMessages = t.HasRestrictedVoiceAndVideoMessages - v.JoinToSendMessages = t.JoinToSendMessages - v.JoinByRequest = t.JoinByRequest - v.Description = t.Description - v.InviteLink = t.InviteLink - v.PinnedMessage = t.PinnedMessage - v.Permissions = t.Permissions - v.SlowModeDelay = t.SlowModeDelay - v.UnrestrictBoostCount = t.UnrestrictBoostCount - v.MessageAutoDeleteTime = t.MessageAutoDeleteTime - v.HasAggressiveAntiSpamEnabled = t.HasAggressiveAntiSpamEnabled - v.HasHiddenMembers = t.HasHiddenMembers - v.HasProtectedContent = t.HasProtectedContent - v.HasVisibleHistory = t.HasVisibleHistory - v.StickerSetName = t.StickerSetName - v.CanSetStickerSet = t.CanSetStickerSet - v.CustomEmojiStickerSetName = t.CustomEmojiStickerSetName - v.LinkedChatId = t.LinkedChatId - v.Location = t.Location + v.InlineMessageId = t.InlineMessageId + v.ChatInstance = t.ChatInstance + v.Data = t.Data + v.GameShortName = t.GameShortName return nil } +// Chat (https://core.telegram.org/bots/api#chat) +// +// This object represents a chat. +type Chat struct { + // Unique identifier for this chat. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this identifier. + Id int64 `json:"id"` + // Type of the chat, can be either "private", "group", "supergroup" or "channel" + Type string `json:"type"` + // Optional. Title, for supergroups, channels and group chats + Title string `json:"title,omitempty"` + // Optional. Username, for private chats, supergroups and channels if available + Username string `json:"username,omitempty"` + // Optional. First name of the other party in a private chat + FirstName string `json:"first_name,omitempty"` + // Optional. Last name of the other party in a private chat + LastName string `json:"last_name,omitempty"` + // Optional. True, if the supergroup chat is a forum (has topics enabled) + IsForum bool `json:"is_forum,omitempty"` +} + // ChatAdministratorRights (https://core.telegram.org/bots/api#chatadministratorrights) // // Represents the rights of an administrator in a chat. @@ -747,7 +1178,7 @@ type ChatAdministratorRights struct { CanInviteUsers bool `json:"can_invite_users"` // True, if the administrator can post stories to the chat CanPostStories bool `json:"can_post_stories"` - // True, if the administrator can edit stories posted by other users + // True, if the administrator can edit stories posted by other users, post stories to the chat page, pin chat stories, and access the chat's story archive CanEditStories bool `json:"can_edit_stories"` // True, if the administrator can delete stories posted by other users CanDeleteStories bool `json:"can_delete_stories"` @@ -761,6 +1192,34 @@ type ChatAdministratorRights struct { CanManageTopics bool `json:"can_manage_topics,omitempty"` } +// ChatBackground (https://core.telegram.org/bots/api#chatbackground) +// +// This object represents a chat background. +type ChatBackground struct { + // Type of the background + Type BackgroundType `json:"type"` +} + +// UnmarshalJSON is a custom JSON unmarshaller to use the helpers which allow for unmarshalling structs into interfaces. +func (v *ChatBackground) UnmarshalJSON(b []byte) error { + // All fields in ChatBackground, with interface fields as json.RawMessage + type tmp struct { + Type json.RawMessage `json:"type"` + } + t := tmp{} + err := json.Unmarshal(b, &t) + if err != nil { + return fmt.Errorf("failed to unmarshal ChatBackground JSON into tmp struct: %w", err) + } + + v.Type, err = unmarshalBackgroundType(t.Type) + if err != nil { + return fmt.Errorf("failed to unmarshal custom JSON field Type: %w", err) + } + + return nil +} + // ChatBoost (https://core.telegram.org/bots/api#chatboost) // // This object contains information about a chat boost. @@ -1091,6 +1550,202 @@ type ChatBoostUpdated struct { Boost ChatBoost `json:"boost"` } +// ChatFullInfo (https://core.telegram.org/bots/api#chatfullinfo) +// +// This object contains full information about a chat. +type ChatFullInfo struct { + // Unique identifier for this chat. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this identifier. + Id int64 `json:"id"` + // Type of the chat, can be either "private", "group", "supergroup" or "channel" + Type string `json:"type"` + // Optional. Title, for supergroups, channels and group chats + Title string `json:"title,omitempty"` + // Optional. Username, for private chats, supergroups and channels if available + Username string `json:"username,omitempty"` + // Optional. First name of the other party in a private chat + FirstName string `json:"first_name,omitempty"` + // Optional. Last name of the other party in a private chat + LastName string `json:"last_name,omitempty"` + // Optional. True, if the supergroup chat is a forum (has topics enabled) + IsForum bool `json:"is_forum,omitempty"` + // Identifier of the accent color for the chat name and backgrounds of the chat photo, reply header, and link preview. See accent colors for more details. + AccentColorId int64 `json:"accent_color_id"` + // The maximum number of reactions that can be set on a message in the chat + MaxReactionCount int64 `json:"max_reaction_count"` + // Optional. Chat photo + Photo *ChatPhoto `json:"photo,omitempty"` + // Optional. If non-empty, the list of all active chat usernames; for private chats, supergroups and channels + ActiveUsernames []string `json:"active_usernames,omitempty"` + // Optional. For private chats, the date of birth of the user + Birthdate *Birthdate `json:"birthdate,omitempty"` + // Optional. For private chats with business accounts, the intro of the business + BusinessIntro *BusinessIntro `json:"business_intro,omitempty"` + // Optional. For private chats with business accounts, the location of the business + BusinessLocation *BusinessLocation `json:"business_location,omitempty"` + // Optional. For private chats with business accounts, the opening hours of the business + BusinessOpeningHours *BusinessOpeningHours `json:"business_opening_hours,omitempty"` + // Optional. For private chats, the personal channel of the user + PersonalChat *Chat `json:"personal_chat,omitempty"` + // Optional. List of available reactions allowed in the chat. If omitted, then all emoji reactions are allowed. + AvailableReactions []ReactionType `json:"available_reactions,omitempty"` + // Optional. Custom emoji identifier of the emoji chosen by the chat for the reply header and link preview background + BackgroundCustomEmojiId string `json:"background_custom_emoji_id,omitempty"` + // Optional. Identifier of the accent color for the chat's profile background. See profile accent colors for more details. + ProfileAccentColorId int64 `json:"profile_accent_color_id,omitempty"` + // Optional. Custom emoji identifier of the emoji chosen by the chat for its profile background + ProfileBackgroundCustomEmojiId string `json:"profile_background_custom_emoji_id,omitempty"` + // Optional. Custom emoji identifier of the emoji status of the chat or the other party in a private chat + EmojiStatusCustomEmojiId string `json:"emoji_status_custom_emoji_id,omitempty"` + // Optional. Expiration date of the emoji status of the chat or the other party in a private chat, in Unix time, if any + EmojiStatusExpirationDate int64 `json:"emoji_status_expiration_date,omitempty"` + // Optional. Bio of the other party in a private chat + Bio string `json:"bio,omitempty"` + // Optional. True, if privacy settings of the other party in the private chat allows to use tg://user?id= links only in chats with the user + HasPrivateForwards bool `json:"has_private_forwards,omitempty"` + // Optional. True, if the privacy settings of the other party restrict sending voice and video note messages in the private chat + HasRestrictedVoiceAndVideoMessages bool `json:"has_restricted_voice_and_video_messages,omitempty"` + // Optional. True, if users need to join the supergroup before they can send messages + JoinToSendMessages bool `json:"join_to_send_messages,omitempty"` + // Optional. True, if all users directly joining the supergroup without using an invite link need to be approved by supergroup administrators + JoinByRequest bool `json:"join_by_request,omitempty"` + // Optional. Description, for groups, supergroups and channel chats + Description string `json:"description,omitempty"` + // Optional. Primary invite link, for groups, supergroups and channel chats + InviteLink string `json:"invite_link,omitempty"` + // Optional. The most recent pinned message (by sending date) + PinnedMessage *Message `json:"pinned_message,omitempty"` + // Optional. Default chat member permissions, for groups and supergroups + Permissions *ChatPermissions `json:"permissions,omitempty"` + // Optional. For supergroups, the minimum allowed delay between consecutive messages sent by each unprivileged user; in seconds + SlowModeDelay int64 `json:"slow_mode_delay,omitempty"` + // Optional. For supergroups, the minimum number of boosts that a non-administrator user needs to add in order to ignore slow mode and chat permissions + UnrestrictBoostCount int64 `json:"unrestrict_boost_count,omitempty"` + // Optional. The time after which all messages sent to the chat will be automatically deleted; in seconds + MessageAutoDeleteTime int64 `json:"message_auto_delete_time,omitempty"` + // Optional. True, if aggressive anti-spam checks are enabled in the supergroup. The field is only available to chat administrators. + HasAggressiveAntiSpamEnabled bool `json:"has_aggressive_anti_spam_enabled,omitempty"` + // Optional. True, if non-administrators can only get the list of bots and administrators in the chat + HasHiddenMembers bool `json:"has_hidden_members,omitempty"` + // Optional. True, if messages from the chat can't be forwarded to other chats + HasProtectedContent bool `json:"has_protected_content,omitempty"` + // Optional. True, if new chat members will have access to old messages; available only to chat administrators + HasVisibleHistory bool `json:"has_visible_history,omitempty"` + // Optional. For supergroups, name of the group sticker set + StickerSetName string `json:"sticker_set_name,omitempty"` + // Optional. True, if the bot can change the group sticker set + CanSetStickerSet bool `json:"can_set_sticker_set,omitempty"` + // Optional. For supergroups, the name of the group's custom emoji sticker set. Custom emoji from this set can be used by all users and bots in the group. + CustomEmojiStickerSetName string `json:"custom_emoji_sticker_set_name,omitempty"` + // Optional. Unique identifier for the linked chat, i.e. the discussion group identifier for a channel and vice versa; for supergroups and channel chats. This identifier may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier. + LinkedChatId int64 `json:"linked_chat_id,omitempty"` + // Optional. For supergroups, the location to which the supergroup is connected + Location *ChatLocation `json:"location,omitempty"` +} + +// UnmarshalJSON is a custom JSON unmarshaller to use the helpers which allow for unmarshalling structs into interfaces. +func (v *ChatFullInfo) UnmarshalJSON(b []byte) error { + // All fields in ChatFullInfo, with interface fields as json.RawMessage + type tmp struct { + Id int64 `json:"id"` + Type string `json:"type"` + Title string `json:"title"` + Username string `json:"username"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + IsForum bool `json:"is_forum"` + AccentColorId int64 `json:"accent_color_id"` + MaxReactionCount int64 `json:"max_reaction_count"` + Photo *ChatPhoto `json:"photo"` + ActiveUsernames []string `json:"active_usernames"` + Birthdate *Birthdate `json:"birthdate"` + BusinessIntro *BusinessIntro `json:"business_intro"` + BusinessLocation *BusinessLocation `json:"business_location"` + BusinessOpeningHours *BusinessOpeningHours `json:"business_opening_hours"` + PersonalChat *Chat `json:"personal_chat"` + AvailableReactions json.RawMessage `json:"available_reactions"` + BackgroundCustomEmojiId string `json:"background_custom_emoji_id"` + ProfileAccentColorId int64 `json:"profile_accent_color_id"` + ProfileBackgroundCustomEmojiId string `json:"profile_background_custom_emoji_id"` + EmojiStatusCustomEmojiId string `json:"emoji_status_custom_emoji_id"` + EmojiStatusExpirationDate int64 `json:"emoji_status_expiration_date"` + Bio string `json:"bio"` + HasPrivateForwards bool `json:"has_private_forwards"` + HasRestrictedVoiceAndVideoMessages bool `json:"has_restricted_voice_and_video_messages"` + JoinToSendMessages bool `json:"join_to_send_messages"` + JoinByRequest bool `json:"join_by_request"` + Description string `json:"description"` + InviteLink string `json:"invite_link"` + PinnedMessage *Message `json:"pinned_message"` + Permissions *ChatPermissions `json:"permissions"` + SlowModeDelay int64 `json:"slow_mode_delay"` + UnrestrictBoostCount int64 `json:"unrestrict_boost_count"` + MessageAutoDeleteTime int64 `json:"message_auto_delete_time"` + HasAggressiveAntiSpamEnabled bool `json:"has_aggressive_anti_spam_enabled"` + HasHiddenMembers bool `json:"has_hidden_members"` + HasProtectedContent bool `json:"has_protected_content"` + HasVisibleHistory bool `json:"has_visible_history"` + StickerSetName string `json:"sticker_set_name"` + CanSetStickerSet bool `json:"can_set_sticker_set"` + CustomEmojiStickerSetName string `json:"custom_emoji_sticker_set_name"` + LinkedChatId int64 `json:"linked_chat_id"` + Location *ChatLocation `json:"location"` + } + t := tmp{} + err := json.Unmarshal(b, &t) + if err != nil { + return fmt.Errorf("failed to unmarshal ChatFullInfo JSON into tmp struct: %w", err) + } + + v.Id = t.Id + v.Type = t.Type + v.Title = t.Title + v.Username = t.Username + v.FirstName = t.FirstName + v.LastName = t.LastName + v.IsForum = t.IsForum + v.AccentColorId = t.AccentColorId + v.MaxReactionCount = t.MaxReactionCount + v.Photo = t.Photo + v.ActiveUsernames = t.ActiveUsernames + v.Birthdate = t.Birthdate + v.BusinessIntro = t.BusinessIntro + v.BusinessLocation = t.BusinessLocation + v.BusinessOpeningHours = t.BusinessOpeningHours + v.PersonalChat = t.PersonalChat + v.AvailableReactions, err = unmarshalReactionTypeArray(t.AvailableReactions) + if err != nil { + return fmt.Errorf("failed to unmarshal custom JSON field AvailableReactions: %w", err) + } + v.BackgroundCustomEmojiId = t.BackgroundCustomEmojiId + v.ProfileAccentColorId = t.ProfileAccentColorId + v.ProfileBackgroundCustomEmojiId = t.ProfileBackgroundCustomEmojiId + v.EmojiStatusCustomEmojiId = t.EmojiStatusCustomEmojiId + v.EmojiStatusExpirationDate = t.EmojiStatusExpirationDate + v.Bio = t.Bio + v.HasPrivateForwards = t.HasPrivateForwards + v.HasRestrictedVoiceAndVideoMessages = t.HasRestrictedVoiceAndVideoMessages + v.JoinToSendMessages = t.JoinToSendMessages + v.JoinByRequest = t.JoinByRequest + v.Description = t.Description + v.InviteLink = t.InviteLink + v.PinnedMessage = t.PinnedMessage + v.Permissions = t.Permissions + v.SlowModeDelay = t.SlowModeDelay + v.UnrestrictBoostCount = t.UnrestrictBoostCount + v.MessageAutoDeleteTime = t.MessageAutoDeleteTime + v.HasAggressiveAntiSpamEnabled = t.HasAggressiveAntiSpamEnabled + v.HasHiddenMembers = t.HasHiddenMembers + v.HasProtectedContent = t.HasProtectedContent + v.HasVisibleHistory = t.HasVisibleHistory + v.StickerSetName = t.StickerSetName + v.CanSetStickerSet = t.CanSetStickerSet + v.CustomEmojiStickerSetName = t.CustomEmojiStickerSetName + v.LinkedChatId = t.LinkedChatId + v.Location = t.Location + + return nil +} + // ChatInviteLink (https://core.telegram.org/bots/api#chatinvitelink) // // Represents an invite link for a chat. @@ -1199,7 +1854,7 @@ type MergedChatMember struct { CanInviteUsers bool `json:"can_invite_users,omitempty"` // Optional. True, if the administrator can post stories to the chat (Only for administrator) CanPostStories bool `json:"can_post_stories,omitempty"` - // Optional. True, if the administrator can edit stories posted by other users (Only for administrator) + // Optional. True, if the administrator can edit stories posted by other users, post stories to the chat page, pin chat stories, and access the chat's story archive (Only for administrator) CanEditStories bool `json:"can_edit_stories,omitempty"` // Optional. True, if the administrator can delete stories posted by other users (Only for administrator) CanDeleteStories bool `json:"can_delete_stories,omitempty"` @@ -1374,7 +2029,7 @@ type ChatMemberAdministrator struct { CanInviteUsers bool `json:"can_invite_users"` // True, if the administrator can post stories to the chat CanPostStories bool `json:"can_post_stories"` - // True, if the administrator can edit stories posted by other users + // True, if the administrator can edit stories posted by other users, post stories to the chat page, pin chat stories, and access the chat's story archive CanEditStories bool `json:"can_edit_stories"` // True, if the administrator can delete stories posted by other users CanDeleteStories bool `json:"can_delete_stories"` @@ -1724,6 +2379,8 @@ type ChatMemberUpdated struct { NewChatMember ChatMember `json:"new_chat_member"` // Optional. Chat invite link, which was used by the user to join the chat; for joining by invite link events only. InviteLink *ChatInviteLink `json:"invite_link,omitempty"` + // Optional. True, if the user joined the chat after sending a direct join request without using an invite link and being approved by an administrator + ViaJoinRequest bool `json:"via_join_request,omitempty"` // Optional. True, if the user joined the chat via a chat folder invite link ViaChatFolderInviteLink bool `json:"via_chat_folder_invite_link,omitempty"` } @@ -1738,6 +2395,7 @@ func (v *ChatMemberUpdated) UnmarshalJSON(b []byte) error { OldChatMember json.RawMessage `json:"old_chat_member"` NewChatMember json.RawMessage `json:"new_chat_member"` InviteLink *ChatInviteLink `json:"invite_link"` + ViaJoinRequest bool `json:"via_join_request"` ViaChatFolderInviteLink bool `json:"via_chat_folder_invite_link"` } t := tmp{} @@ -1758,6 +2416,7 @@ func (v *ChatMemberUpdated) UnmarshalJSON(b []byte) error { return fmt.Errorf("failed to unmarshal custom JSON field NewChatMember: %w", err) } v.InviteLink = t.InviteLink + v.ViaJoinRequest = t.ViaJoinRequest v.ViaChatFolderInviteLink = t.ViaChatFolderInviteLink return nil @@ -2058,7 +2717,7 @@ type File struct { // ForceReply (https://core.telegram.org/bots/api#forcereply) // -// Upon receiving a message with this object, Telegram clients will display a reply interface to the user (act as if the user has selected the bot's message and tapped 'Reply'). This can be extremely useful if you want to create user-friendly step-by-step interfaces without having to sacrifice privacy mode. +// Upon receiving a message with this object, Telegram clients will display a reply interface to the user (act as if the user has selected the bot's message and tapped 'Reply'). This can be extremely useful if you want to create user-friendly step-by-step interfaces without having to sacrifice privacy mode. Not supported in channels and for messages sent on behalf of a Telegram Business account. type ForceReply struct { // Shows reply interface to the user, as if they manually selected the bot's message and tapped 'Reply' ForceReply bool `json:"force_reply"` @@ -2262,17 +2921,17 @@ type InlineKeyboardButton struct { Text string `json:"text"` // Optional. HTTP or tg:// URL to be opened when the button is pressed. Links tg://user?id= can be used to mention a user by their identifier without using a username, if this is allowed by their privacy settings. Url string `json:"url,omitempty"` - // Optional. Data to be sent in a callback query to the bot when button is pressed, 1-64 bytes + // Optional. Data to be sent in a callback query to the bot when button is pressed, 1-64 bytes. Not supported for messages sent on behalf of a Telegram Business account. CallbackData string `json:"callback_data,omitempty"` - // Optional. Description of the Web App that will be launched when the user presses the button. The Web App will be able to send an arbitrary message on behalf of the user using the method answerWebAppQuery. Available only in private chats between a user and the bot. + // Optional. Description of the Web App that will be launched when the user presses the button. The Web App will be able to send an arbitrary message on behalf of the user using the method answerWebAppQuery. Available only in private chats between a user and the bot. Not supported for messages sent on behalf of a Telegram Business account. WebApp *WebAppInfo `json:"web_app,omitempty"` // Optional. An HTTPS URL used to automatically authorize the user. Can be used as a replacement for the Telegram Login Widget. LoginUrl *LoginUrl `json:"login_url,omitempty"` - // Optional. If set, pressing the button will prompt the user to select one of their chats, open that chat and insert the bot's username and the specified inline query in the input field. May be empty, in which case just the bot's username will be inserted. + // Optional. If set, pressing the button will prompt the user to select one of their chats, open that chat and insert the bot's username and the specified inline query in the input field. May be empty, in which case just the bot's username will be inserted. Not supported for messages sent on behalf of a Telegram Business account. SwitchInlineQuery *string `json:"switch_inline_query,omitempty"` - // Optional. If set, pressing the button will insert the bot's username and the specified inline query in the current chat's input field. May be empty, in which case only the bot's username will be inserted. This offers a quick way for the user to open your bot in inline mode in the same chat - good for selecting something from multiple options. + // Optional. If set, pressing the button will insert the bot's username and the specified inline query in the current chat's input field. May be empty, in which case only the bot's username will be inserted. This offers a quick way for the user to open your bot in inline mode in the same chat - good for selecting something from multiple options. Not supported in channels and for messages sent on behalf of a Telegram Business account. SwitchInlineQueryCurrentChat *string `json:"switch_inline_query_current_chat,omitempty"` - // Optional. If set, pressing the button will prompt the user to select one of their chats of the specified type, open that chat and insert the bot's username and the specified inline query in the input field + // Optional. If set, pressing the button will prompt the user to select one of their chats of the specified type, open that chat and insert the bot's username and the specified inline query in the input field. Not supported for messages sent on behalf of a Telegram Business account. SwitchInlineQueryChosenChat *SwitchInlineQueryChosenChat `json:"switch_inline_query_chosen_chat,omitempty"` // Optional. Description of the game that will be launched when the user presses the button. NOTE: This type of button must always be the first button in the first row. CallbackGame *CallbackGame `json:"callback_game,omitempty"` @@ -2449,7 +3108,7 @@ type MergedInlineQueryResult struct { Longitude float64 `json:"longitude,omitempty"` // Optional. The radius of uncertainty for the location, measured in meters; 0-1500 (Only for location) HorizontalAccuracy float64 `json:"horizontal_accuracy,omitempty"` - // Optional. Period in seconds for which the location can be updated, should be between 60 and 86400. (Only for location) + // Optional. Period in seconds during which the location can be updated, should be between 60 and 86400, or 0x7FFFFFFF for live locations that can be edited indefinitely. (Only for location) LivePeriod int64 `json:"live_period,omitempty"` // Optional. For live locations, a direction in which the user is moving, in degrees. Must be between 1 and 360 if specified. (Only for location) Heading int64 `json:"heading,omitempty"` @@ -3434,7 +4093,7 @@ type InlineQueryResultLocation struct { Title string `json:"title"` // Optional. The radius of uncertainty for the location, measured in meters; 0-1500 HorizontalAccuracy float64 `json:"horizontal_accuracy,omitempty"` - // Optional. Period in seconds for which the location can be updated, should be between 60 and 86400. + // Optional. Period in seconds during which the location can be updated, should be between 60 and 86400, or 0x7FFFFFFF for live locations that can be edited indefinitely. LivePeriod int64 `json:"live_period,omitempty"` // Optional. For live locations, a direction in which the user is moving, in degrees. Must be between 1 and 360 if specified. Heading int64 `json:"heading,omitempty"` @@ -3972,7 +4631,7 @@ type InputLocationMessageContent struct { Longitude float64 `json:"longitude"` // Optional. The radius of uncertainty for the location, measured in meters; 0-1500 HorizontalAccuracy float64 `json:"horizontal_accuracy,omitempty"` - // Optional. Period in seconds for which the location can be updated, should be between 60 and 86400. + // Optional. Period in seconds during which the location can be updated, should be between 60 and 86400, or 0x7FFFFFFF for live locations that can be edited indefinitely. LivePeriod int64 `json:"live_period,omitempty"` // Optional. For live locations, a direction in which the user is moving, in degrees. Must be between 1 and 360 if specified. Heading int64 `json:"heading,omitempty"` @@ -4502,6 +5161,18 @@ var ( _ InputMessageContent = InputInvoiceMessageContent{} ) +// InputPollOption (https://core.telegram.org/bots/api#inputpolloption) +// +// This object contains information about one answer option in a poll to send. +type InputPollOption struct { + // Option text, 1-100 characters + Text string `json:"text"` + // Optional. Mode for parsing entities in the text. See formatting options for more details. Currently, only custom emoji entities are allowed + TextParseMode string `json:"text_parse_mode,omitempty"` + // Optional. A JSON-serialized list of special entities that appear in the poll option text. It can be specified instead of text_parse_mode + TextEntities []MessageEntity `json:"text_entities,omitempty"` +} + // InputSticker (https://core.telegram.org/bots/api#inputsticker) // // This object describes a sticker to be added to a sticker set. @@ -5146,6 +5817,8 @@ type Message struct { ProximityAlertTriggered *ProximityAlertTriggered `json:"proximity_alert_triggered,omitempty"` // Optional. Service message: user boosted the chat BoostAdded *ChatBoostAdded `json:"boost_added,omitempty"` + // Optional. Service message: chat background set + ChatBackgroundSet *ChatBackground `json:"chat_background_set,omitempty"` // Optional. Service message: forum topic created ForumTopicCreated *ForumTopicCreated `json:"forum_topic_created,omitempty"` // Optional. Service message: forum topic edited @@ -5248,6 +5921,7 @@ func (v *Message) UnmarshalJSON(b []byte) error { PassportData *PassportData `json:"passport_data"` ProximityAlertTriggered *ProximityAlertTriggered `json:"proximity_alert_triggered"` BoostAdded *ChatBoostAdded `json:"boost_added"` + ChatBackgroundSet *ChatBackground `json:"chat_background_set"` ForumTopicCreated *ForumTopicCreated `json:"forum_topic_created"` ForumTopicEdited *ForumTopicEdited `json:"forum_topic_edited"` ForumTopicClosed *ForumTopicClosed `json:"forum_topic_closed"` @@ -5341,6 +6015,7 @@ func (v *Message) UnmarshalJSON(b []byte) error { v.PassportData = t.PassportData v.ProximityAlertTriggered = t.ProximityAlertTriggered v.BoostAdded = t.BoostAdded + v.ChatBackgroundSet = t.ChatBackgroundSet v.ForumTopicCreated = t.ForumTopicCreated v.ForumTopicEdited = t.ForumTopicEdited v.ForumTopicClosed = t.ForumTopicClosed @@ -6432,6 +7107,8 @@ type Poll struct { Id string `json:"id"` // Poll question, 1-300 characters Question string `json:"question"` + // Optional. Special entities that appear in the question. Currently, only custom emoji entities are allowed in poll questions + QuestionEntities []MessageEntity `json:"question_entities,omitempty"` // List of poll options Options []PollOption `json:"options,omitempty"` // Total number of users that voted in the poll @@ -6476,6 +7153,8 @@ type PollAnswer struct { type PollOption struct { // Option text, 1-100 characters Text string `json:"text"` + // Optional. Special entities that appear in the option text. Currently, only custom emoji entities are allowed in poll option texts + TextEntities []MessageEntity `json:"text_entities,omitempty"` // Number of users that voted for this option VoterCount int64 `json:"voter_count"` } @@ -6723,7 +7402,7 @@ func (v ReactionTypeEmoji) reactionType() {} // ReplyKeyboardMarkup (https://core.telegram.org/bots/api#replykeyboardmarkup) // -// This object represents a custom keyboard with reply options (see Introduction to bots for details and examples). +// This object represents a custom keyboard with reply options (see Introduction to bots for details and examples). Not supported in channels and for messages sent on behalf of a Telegram Business account. type ReplyKeyboardMarkup struct { // Array of button rows, each represented by an Array of KeyboardButton objects Keyboard [][]KeyboardButton `json:"keyboard,omitempty"` @@ -6744,7 +7423,7 @@ func (v ReplyKeyboardMarkup) replyMarkup() {} // ReplyKeyboardRemove (https://core.telegram.org/bots/api#replykeyboardremove) // -// Upon receiving a message with this object, Telegram clients will remove the current custom keyboard and display the default letter-keyboard. By default, custom keyboards are displayed until a new keyboard is sent by a bot. An exception is made for one-time keyboards that are hidden immediately after the user presses a button (see ReplyKeyboardMarkup). +// Upon receiving a message with this object, Telegram clients will remove the current custom keyboard and display the default letter-keyboard. By default, custom keyboards are displayed until a new keyboard is sent by a bot. An exception is made for one-time keyboards that are hidden immediately after the user presses a button (see ReplyKeyboardMarkup). Not supported in channels and for messages sent on behalf of a Telegram Business account. type ReplyKeyboardRemove struct { // Requests clients to remove the custom keyboard (user will not be able to summon this keyboard; if you want to hide the keyboard from sight but keep it accessible, use one_time_keyboard in ReplyKeyboardMarkup) RemoveKeyboard bool `json:"remove_keyboard"` @@ -6761,7 +7440,7 @@ func (v ReplyKeyboardRemove) replyMarkup() {} type ReplyParameters struct { // Identifier of the message that will be replied to in the current chat, or in the chat chat_id if it is specified MessageId int64 `json:"message_id"` - // Optional. If the message to be replied to is from a different chat, unique identifier for the chat or username of the channel (in the format @channelusername). Not supported for messages sent on behalf of a business account. + // Optional. If the message to be replied to is from a different chat, unique identifier for the chat. Not supported for messages sent on behalf of a business account. ChatId int64 `json:"chat_id,omitempty"` // Optional. Pass True if the message should be sent even if the specified message to be replied to is not found. Always False for replies in another chat or forum topic. Always True for messages sent on behalf of a business account. AllowSendingWithoutReply bool `json:"allow_sending_without_reply,omitempty"` @@ -6982,7 +7661,7 @@ type Update struct { EditedChannelPost *Message `json:"edited_channel_post,omitempty"` // Optional. The bot was connected to or disconnected from a business account, or a user edited an existing connection with the bot BusinessConnection *BusinessConnection `json:"business_connection,omitempty"` - // Optional. New non-service message from a connected business account + // Optional. New message from a connected business account BusinessMessage *Message `json:"business_message,omitempty"` // Optional. New version of a message from a connected business account EditedBusinessMessage *Message `json:"edited_business_message,omitempty"` diff --git a/vendor/github.com/PaulSonOfLars/gotgbot/v2/spec_commit b/vendor/github.com/PaulSonOfLars/gotgbot/v2/spec_commit index 9d4b8bef..1b7715e6 100644 --- a/vendor/github.com/PaulSonOfLars/gotgbot/v2/spec_commit +++ b/vendor/github.com/PaulSonOfLars/gotgbot/v2/spec_commit @@ -1 +1 @@ -93db1105f667e10ee0c699248aae12f30d310cc9 \ No newline at end of file +68843d1ae456b90a494bfee92253f30a8204b2a7 \ No newline at end of file diff --git a/vendor/github.com/go-sql-driver/mysql/AUTHORS b/vendor/github.com/go-sql-driver/mysql/AUTHORS index 05132751..4021b96c 100644 --- a/vendor/github.com/go-sql-driver/mysql/AUTHORS +++ b/vendor/github.com/go-sql-driver/mysql/AUTHORS @@ -13,6 +13,7 @@ Aaron Hopkins Achille Roussel +Aidan Alex Snast Alexey Palazhchenko Andrew Reid @@ -20,12 +21,14 @@ Animesh Ray Arne Hormann Ariel Mashraki Asta Xie +Brian Hendriks Bulat Gaifullin Caine Jette Carlos Nieto Chris Kirkland Chris Moos Craig Wilson +Daemonxiao <735462752 at qq.com> Daniel Montoya Daniel Nichter Daniël van Eeden @@ -33,9 +36,11 @@ Dave Protasowski DisposaBoy Egor Smolyakov Erwan Martin +Evan Elias Evan Shaw Frederick Mayle Gustavo Kristic +Gusted Hajime Nakagami Hanno Braun Henri Yandell @@ -47,8 +52,11 @@ INADA Naoki Jacek Szwec James Harr Janek Vedock +Jason Ng +Jean-Yves Pellé Jeff Hodges Jeffrey Charles +Jennifer Purevsuren Jerome Meyer Jiajia Zhong Jian Zhen @@ -74,15 +82,19 @@ Maciej Zimnoch Michael Woolnough Nathanial Murphy Nicola Peduzzi +Oliver Bone Olivier Mengué oscarzhao Paul Bonser +Paulius Lozys Peter Schultz +Phil Porada Rebecca Chin Reed Allman Richard Wilkes Robert Russell Runrioter Wung +Samantha Frank Santhosh Kumar Tekuri Sho Iizuka Sho Ikeda @@ -93,6 +105,7 @@ Stan Putrya Stanley Gunawan Steven Hartland Tan Jinhua <312841925 at qq.com> +Tetsuro Aoki Thomas Wodarek Tim Ruffles Tom Jenkinson @@ -102,6 +115,7 @@ Xiangyu Hu Xiaobing Jiang Xiuming Chen Xuehong Chan +Zhang Xiang Zhenye Xie Zhixin Wen Ziheng Lyu @@ -111,14 +125,18 @@ Ziheng Lyu Barracuda Networks, Inc. Counting Ltd. DigitalOcean Inc. +Dolthub Inc. dyves labs AG Facebook Inc. GitHub Inc. Google Inc. InfoSum Ltd. Keybase Inc. +Microsoft Corp. Multiplay Ltd. Percona LLC +PingCAP Inc. Pivotal Inc. +Shattered Silicon Ltd. Stripe Inc. Zendesk Inc. diff --git a/vendor/github.com/go-sql-driver/mysql/CHANGELOG.md b/vendor/github.com/go-sql-driver/mysql/CHANGELOG.md index 77024a82..0c9bd9b1 100644 --- a/vendor/github.com/go-sql-driver/mysql/CHANGELOG.md +++ b/vendor/github.com/go-sql-driver/mysql/CHANGELOG.md @@ -1,3 +1,58 @@ +## Version 1.8.1 (2024-03-26) + +Bugfixes: + +- fix race condition when context is canceled in [#1562](https://github.com/go-sql-driver/mysql/pull/1562) and [#1570](https://github.com/go-sql-driver/mysql/pull/1570) + +## Version 1.8.0 (2024-03-09) + +Major Changes: + +- Use `SET NAMES charset COLLATE collation`. by @methane in [#1437](https://github.com/go-sql-driver/mysql/pull/1437) + - Older go-mysql-driver used `collation_id` in the handshake packet. But it caused collation mismatch in some situation. + - If you don't specify charset nor collation, go-mysql-driver sends `SET NAMES utf8mb4` for new connection. This uses server's default collation for utf8mb4. + - If you specify charset, go-mysql-driver sends `SET NAMES `. This uses the server's default collation for ``. + - If you specify collation and/or charset, go-mysql-driver sends `SET NAMES charset COLLATE collation`. +- PathEscape dbname in DSN. by @methane in [#1432](https://github.com/go-sql-driver/mysql/pull/1432) + - This is backward incompatible in rare case. Check your DSN. +- Drop Go 1.13-17 support by @methane in [#1420](https://github.com/go-sql-driver/mysql/pull/1420) + - Use Go 1.18+ +- Parse numbers on text protocol too by @methane in [#1452](https://github.com/go-sql-driver/mysql/pull/1452) + - When text protocol is used, go-mysql-driver passed bare `[]byte` to database/sql for avoid unnecessary allocation and conversion. + - If user specified `*any` to `Scan()`, database/sql passed the `[]byte` into the target variable. + - This confused users because most user doesn't know when text/binary protocol used. + - go-mysql-driver 1.8 converts integer/float values into int64/double even in text protocol. This doesn't increase allocation compared to `[]byte` and conversion cost is negatable. +- New options start using the Functional Option Pattern to avoid increasing technical debt in the Config object. Future version may introduce Functional Option for existing options, but not for now. + - Make TimeTruncate functional option by @methane in [1552](https://github.com/go-sql-driver/mysql/pull/1552) + - Add BeforeConnect callback to configuration object by @ItalyPaleAle in [#1469](https://github.com/go-sql-driver/mysql/pull/1469) + + +Other changes: + +- Adding DeregisterDialContext to prevent memory leaks with dialers we don't need anymore by @jypelle in https://github.com/go-sql-driver/mysql/pull/1422 +- Make logger configurable per connection by @frozenbonito in https://github.com/go-sql-driver/mysql/pull/1408 +- Fix ColumnType.DatabaseTypeName for mediumint unsigned by @evanelias in https://github.com/go-sql-driver/mysql/pull/1428 +- Add connection attributes by @Daemonxiao in https://github.com/go-sql-driver/mysql/pull/1389 +- Stop `ColumnTypeScanType()` from returning `sql.RawBytes` by @methane in https://github.com/go-sql-driver/mysql/pull/1424 +- Exec() now provides access to status of multiple statements. by @mherr-google in https://github.com/go-sql-driver/mysql/pull/1309 +- Allow to change (or disable) the default driver name for registration by @dolmen in https://github.com/go-sql-driver/mysql/pull/1499 +- Add default connection attribute '_server_host' by @oblitorum in https://github.com/go-sql-driver/mysql/pull/1506 +- QueryUnescape DSN ConnectionAttribute value by @zhangyangyu in https://github.com/go-sql-driver/mysql/pull/1470 +- Add client_ed25519 authentication by @Gusted in https://github.com/go-sql-driver/mysql/pull/1518 + +## Version 1.7.1 (2023-04-25) + +Changes: + + - bump actions/checkout@v3 and actions/setup-go@v3 (#1375) + - Add go1.20 and mariadb10.11 to the testing matrix (#1403) + - Increase default maxAllowedPacket size. (#1411) + +Bugfixes: + + - Use SET syntax as specified in the MySQL documentation (#1402) + + ## Version 1.7 (2022-11-29) Changes: @@ -149,7 +204,7 @@ New Features: - Enable microsecond resolution on TIME, DATETIME and TIMESTAMP (#249) - Support for returning table alias on Columns() (#289, #359, #382) - - Placeholder interpolation, can be actived with the DSN parameter `interpolateParams=true` (#309, #318, #490) + - Placeholder interpolation, can be activated with the DSN parameter `interpolateParams=true` (#309, #318, #490) - Support for uint64 parameters with high bit set (#332, #345) - Cleartext authentication plugin support (#327) - Exported ParseDSN function and the Config struct (#403, #419, #429) @@ -193,7 +248,7 @@ Changes: - Also exported the MySQLWarning type - mysqlConn.Close returns the first error encountered instead of ignoring all errors - writePacket() automatically writes the packet size to the header - - readPacket() uses an iterative approach instead of the recursive approach to merge splitted packets + - readPacket() uses an iterative approach instead of the recursive approach to merge split packets New Features: @@ -241,7 +296,7 @@ Bugfixes: - Fixed MySQL 4.1 support: MySQL 4.1 sends packets with lengths which differ from the specification - Convert to DB timezone when inserting `time.Time` - - Splitted packets (more than 16MB) are now merged correctly + - Split packets (more than 16MB) are now merged correctly - Fixed false positive `io.EOF` errors when the data was fully read - Avoid panics on reuse of closed connections - Fixed empty string producing false nil values diff --git a/vendor/github.com/go-sql-driver/mysql/README.md b/vendor/github.com/go-sql-driver/mysql/README.md index 25de2e5a..4968cb06 100644 --- a/vendor/github.com/go-sql-driver/mysql/README.md +++ b/vendor/github.com/go-sql-driver/mysql/README.md @@ -40,15 +40,23 @@ A MySQL-Driver for Go's [database/sql](https://golang.org/pkg/database/sql/) pac * Optional placeholder interpolation ## Requirements - * Go 1.13 or higher. We aim to support the 3 latest versions of Go. - * MySQL (4.1+), MariaDB, Percona Server, Google CloudSQL or Sphinx (2.2.3+) + +* Go 1.19 or higher. We aim to support the 3 latest versions of Go. +* MySQL (5.7+) and MariaDB (10.3+) are supported. +* [TiDB](https://github.com/pingcap/tidb) is supported by PingCAP. + * Do not ask questions about TiDB in our issue tracker or forum. + * [Document](https://docs.pingcap.com/tidb/v6.1/dev-guide-sample-application-golang) + * [Forum](https://ask.pingcap.com/) +* go-mysql would work with Percona Server, Google CloudSQL or Sphinx (2.2.3+). + * Maintainers won't support them. Do not expect issues are investigated and resolved by maintainers. + * Investigate issues yourself and please send a pull request to fix it. --------------------------------------- ## Installation Simple install the package to your [$GOPATH](https://github.com/golang/go/wiki/GOPATH "GOPATH") with the [go tool](https://golang.org/cmd/go/ "go command") from shell: ```bash -$ go get -u github.com/go-sql-driver/mysql +go get -u github.com/go-sql-driver/mysql ``` Make sure [Git is installed](https://git-scm.com/downloads) on your machine and in your system's `PATH`. @@ -114,6 +122,12 @@ This has the same effect as an empty DSN string: ``` +`dbname` is escaped by [PathEscape()](https://pkg.go.dev/net/url#PathEscape) since v1.8.0. If your database name is `dbname/withslash`, it becomes: + +``` +/dbname%2Fwithslash +``` + Alternatively, [Config.FormatDSN](https://godoc.org/github.com/go-sql-driver/mysql#Config.FormatDSN) can be used to create a DSN string by filling a struct. #### Password @@ -121,7 +135,7 @@ Passwords can consist of any character. Escaping is **not** necessary. #### Protocol See [net.Dial](https://golang.org/pkg/net/#Dial) for more information which networks are available. -In general you should use an Unix domain socket if available and TCP otherwise for best performance. +In general you should use a Unix domain socket if available and TCP otherwise for best performance. #### Address For TCP and UDP networks, addresses have the form `host[:port]`. @@ -145,7 +159,7 @@ Default: false ``` `allowAllFiles=true` disables the file allowlist for `LOAD DATA LOCAL INFILE` and allows *all* files. -[*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html) +[*Might be insecure!*](https://dev.mysql.com/doc/refman/8.0/en/load-data.html#load-data-local) ##### `allowCleartextPasswords` @@ -194,10 +208,9 @@ Valid Values: Default: none ``` -Sets the charset used for client-server interaction (`"SET NAMES "`). If multiple charsets are set (separated by a comma), the following charset is used if setting the charset failes. This enables for example support for `utf8mb4` ([introduced in MySQL 5.5.3](http://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html)) with fallback to `utf8` for older servers (`charset=utf8mb4,utf8`). +Sets the charset used for client-server interaction (`"SET NAMES "`). If multiple charsets are set (separated by a comma), the following charset is used if setting the charset fails. This enables for example support for `utf8mb4` ([introduced in MySQL 5.5.3](http://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html)) with fallback to `utf8` for older servers (`charset=utf8mb4,utf8`). -Usage of the `charset` parameter is discouraged because it issues additional queries to the server. -Unless you need the fallback behavior, please use `collation` instead. +See also [Unicode Support](#unicode-support). ##### `checkConnLiveness` @@ -226,6 +239,7 @@ The default collation (`utf8mb4_general_ci`) is supported from MySQL 5.5. You s Collations for charset "ucs2", "utf16", "utf16le", and "utf32" can not be used ([ref](https://dev.mysql.com/doc/refman/5.7/en/charset-connection.html#charset-connection-impermissible-client-charset)). +See also [Unicode Support](#unicode-support). ##### `clientFoundRows` @@ -279,13 +293,22 @@ Note that this sets the location for time.Time values but does not change MySQL' Please keep in mind, that param values must be [url.QueryEscape](https://golang.org/pkg/net/url/#QueryEscape)'ed. Alternatively you can manually replace the `/` with `%2F`. For example `US/Pacific` would be `loc=US%2FPacific`. +##### `timeTruncate` + +``` +Type: duration +Default: 0 +``` + +[Truncate time values](https://pkg.go.dev/time#Duration.Truncate) to the specified duration. The value must be a decimal number with a unit suffix (*"ms"*, *"s"*, *"m"*, *"h"*), such as *"30s"*, *"0.5m"* or *"1m30s"*. + ##### `maxAllowedPacket` ``` Type: decimal number -Default: 4194304 +Default: 64*1024*1024 ``` -Max packet size allowed in bytes. The default value is 4 MiB and should be adjusted to match the server settings. `maxAllowedPacket=0` can be used to automatically fetch the `max_allowed_packet` variable from server *on every connection*. +Max packet size allowed in bytes. The default value is 64 MiB and should be adjusted to match the server settings. `maxAllowedPacket=0` can be used to automatically fetch the `max_allowed_packet` variable from server *on every connection*. ##### `multiStatements` @@ -295,9 +318,25 @@ Valid Values: true, false Default: false ``` -Allow multiple statements in one query. While this allows batch queries, it also greatly increases the risk of SQL injections. Only the result of the first query is returned, all other results are silently discarded. +Allow multiple statements in one query. This can be used to bach multiple queries. Use [Rows.NextResultSet()](https://pkg.go.dev/database/sql#Rows.NextResultSet) to get result of the second and subsequent queries. + +When `multiStatements` is used, `?` parameters must only be used in the first statement. [interpolateParams](#interpolateparams) can be used to avoid this limitation unless prepared statement is used explicitly. + +It's possible to access the last inserted ID and number of affected rows for multiple statements by using `sql.Conn.Raw()` and the `mysql.Result`. For example: -When `multiStatements` is used, `?` parameters must only be used in the first statement. +```go +conn, _ := db.Conn(ctx) +conn.Raw(func(conn any) error { + ex := conn.(driver.Execer) + res, err := ex.Exec(` + UPDATE point SET x = 1 WHERE y = 2; + UPDATE point SET x = 2 WHERE y = 3; + `, nil) + // Both slices have 2 elements. + log.Print(res.(mysql.Result).AllRowsAffected()) + log.Print(res.(mysql.Result).AllLastInsertIds()) +}) +``` ##### `parseTime` @@ -393,6 +432,15 @@ Default: 0 I/O write timeout. The value must be a decimal number with a unit suffix (*"ms"*, *"s"*, *"m"*, *"h"*), such as *"30s"*, *"0.5m"* or *"1m30s"*. +##### `connectionAttributes` + +``` +Type: comma-delimited string of user-defined "key:value" pairs +Valid Values: (:,:,...) +Default: none +``` + +[Connection attributes](https://dev.mysql.com/doc/refman/8.0/en/performance-schema-connection-attribute-tables.html) are key-value pairs that application programs can pass to the server at connect time. ##### System Variables @@ -465,7 +513,7 @@ user:password@/ The connection pool is managed by Go's database/sql package. For details on how to configure the size of the pool and how long connections stay in the pool see `*DB.SetMaxOpenConns`, `*DB.SetMaxIdleConns`, and `*DB.SetConnMaxLifetime` in the [database/sql documentation](https://golang.org/pkg/database/sql/). The read, write, and dial timeouts for each individual connection are configured with the DSN parameters [`readTimeout`](#readtimeout), [`writeTimeout`](#writetimeout), and [`timeout`](#timeout), respectively. ## `ColumnType` Support -This driver supports the [`ColumnType` interface](https://golang.org/pkg/database/sql/#ColumnType) introduced in Go 1.8, with the exception of [`ColumnType.Length()`](https://golang.org/pkg/database/sql/#ColumnType.Length), which is currently not supported. All Unsigned database type names will be returned `UNSIGNED ` with `INT`, `TINYINT`, `SMALLINT`, `BIGINT`. +This driver supports the [`ColumnType` interface](https://golang.org/pkg/database/sql/#ColumnType) introduced in Go 1.8, with the exception of [`ColumnType.Length()`](https://golang.org/pkg/database/sql/#ColumnType.Length), which is currently not supported. All Unsigned database type names will be returned `UNSIGNED ` with `INT`, `TINYINT`, `SMALLINT`, `MEDIUMINT`, `BIGINT`. ## `context.Context` Support Go 1.8 added `database/sql` support for `context.Context`. This driver supports query timeouts and cancellation via contexts. @@ -478,7 +526,7 @@ For this feature you need direct access to the package. Therefore you must chang import "github.com/go-sql-driver/mysql" ``` -Files must be explicitly allowed by registering them with `mysql.RegisterLocalFile(filepath)` (recommended) or the allowlist check must be deactivated by using the DSN parameter `allowAllFiles=true` ([*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)). +Files must be explicitly allowed by registering them with `mysql.RegisterLocalFile(filepath)` (recommended) or the allowlist check must be deactivated by using the DSN parameter `allowAllFiles=true` ([*Might be insecure!*](https://dev.mysql.com/doc/refman/8.0/en/load-data.html#load-data-local)). To use a `io.Reader` a handler function must be registered with `mysql.RegisterReaderHandler(name, handler)` which returns a `io.Reader` or `io.ReadCloser`. The Reader is available with the filepath `Reader::` then. Choose different names for different handlers and `DeregisterReaderHandler` when you don't need it anymore. @@ -496,9 +544,11 @@ However, many want to scan MySQL `DATE` and `DATETIME` values into `time.Time` v ### Unicode support Since version 1.5 Go-MySQL-Driver automatically uses the collation ` utf8mb4_general_ci` by default. -Other collations / charsets can be set using the [`collation`](#collation) DSN parameter. +Other charsets / collations can be set using the [`charset`](#charset) or [`collation`](#collation) DSN parameter. -Version 1.0 of the driver recommended adding `&charset=utf8` (alias for `SET NAMES utf8`) to the DSN to enable proper UTF-8 support. This is not necessary anymore. The [`collation`](#collation) parameter should be preferred to set another collation / charset than the default. +- When only the `charset` is specified, the `SET NAMES ` query is sent and the server's default collation is used. +- When both the `charset` and `collation` are specified, the `SET NAMES COLLATE ` query is sent. +- When only the `collation` is specified, the collation is specified in the protocol handshake and the `SET NAMES` query is not sent. This can save one roundtrip, but note that the server may ignore the specified collation silently and use the server's default charset/collation instead. See http://dev.mysql.com/doc/refman/8.0/en/charset-unicode.html for more details on MySQL's Unicode support. diff --git a/vendor/github.com/go-sql-driver/mysql/auth.go b/vendor/github.com/go-sql-driver/mysql/auth.go index 1ff203e5..74e1bd03 100644 --- a/vendor/github.com/go-sql-driver/mysql/auth.go +++ b/vendor/github.com/go-sql-driver/mysql/auth.go @@ -13,10 +13,13 @@ import ( "crypto/rsa" "crypto/sha1" "crypto/sha256" + "crypto/sha512" "crypto/x509" "encoding/pem" "fmt" "sync" + + "filippo.io/edwards25519" ) // server pub keys registry @@ -33,7 +36,7 @@ var ( // Note: The provided rsa.PublicKey instance is exclusively owned by the driver // after registering it and may not be modified. // -// data, err := ioutil.ReadFile("mykey.pem") +// data, err := os.ReadFile("mykey.pem") // if err != nil { // log.Fatal(err) // } @@ -225,6 +228,44 @@ func encryptPassword(password string, seed []byte, pub *rsa.PublicKey) ([]byte, return rsa.EncryptOAEP(sha1, rand.Reader, pub, plain, nil) } +// authEd25519 does ed25519 authentication used by MariaDB. +func authEd25519(scramble []byte, password string) ([]byte, error) { + // Derived from https://github.com/MariaDB/server/blob/d8e6bb00888b1f82c031938f4c8ac5d97f6874c3/plugin/auth_ed25519/ref10/sign.c + // Code style is from https://cs.opensource.google/go/go/+/refs/tags/go1.21.5:src/crypto/ed25519/ed25519.go;l=207 + h := sha512.Sum512([]byte(password)) + + s, err := edwards25519.NewScalar().SetBytesWithClamping(h[:32]) + if err != nil { + return nil, err + } + A := (&edwards25519.Point{}).ScalarBaseMult(s) + + mh := sha512.New() + mh.Write(h[32:]) + mh.Write(scramble) + messageDigest := mh.Sum(nil) + r, err := edwards25519.NewScalar().SetUniformBytes(messageDigest) + if err != nil { + return nil, err + } + + R := (&edwards25519.Point{}).ScalarBaseMult(r) + + kh := sha512.New() + kh.Write(R.Bytes()) + kh.Write(A.Bytes()) + kh.Write(scramble) + hramDigest := kh.Sum(nil) + k, err := edwards25519.NewScalar().SetUniformBytes(hramDigest) + if err != nil { + return nil, err + } + + S := k.MultiplyAdd(k, s, r) + + return append(R.Bytes(), S.Bytes()...), nil +} + func (mc *mysqlConn) sendEncryptedPassword(seed []byte, pub *rsa.PublicKey) error { enc, err := encryptPassword(mc.cfg.Passwd, seed, pub) if err != nil { @@ -290,8 +331,14 @@ func (mc *mysqlConn) auth(authData []byte, plugin string) ([]byte, error) { enc, err := encryptPassword(mc.cfg.Passwd, authData, pubKey) return enc, err + case "client_ed25519": + if len(authData) != 32 { + return nil, ErrMalformPkt + } + return authEd25519(authData, mc.cfg.Passwd) + default: - errLog.Print("unknown auth plugin:", plugin) + mc.log("unknown auth plugin:", plugin) return nil, ErrUnknownPlugin } } @@ -338,7 +385,7 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error { switch plugin { - // https://insidemysql.com/preparing-your-community-connector-for-mysql-8-part-2-sha256/ + // https://dev.mysql.com/blog-archive/preparing-your-community-connector-for-mysql-8-part-2-sha256/ case "caching_sha2_password": switch len(authData) { case 0: @@ -346,7 +393,7 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error { case 1: switch authData[0] { case cachingSha2PasswordFastAuthSuccess: - if err = mc.readResultOK(); err == nil { + if err = mc.resultUnchanged().readResultOK(); err == nil { return nil // auth successful } @@ -376,13 +423,13 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error { } if data[0] != iAuthMoreData { - return fmt.Errorf("unexpect resp from server for caching_sha2_password perform full authentication") + return fmt.Errorf("unexpected resp from server for caching_sha2_password, perform full authentication") } // parse public key block, rest := pem.Decode(data[1:]) if block == nil { - return fmt.Errorf("No Pem data found, data: %s", rest) + return fmt.Errorf("no pem data found, data: %s", rest) } pkix, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { @@ -397,7 +444,7 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error { return err } } - return mc.readResultOK() + return mc.resultUnchanged().readResultOK() default: return ErrMalformPkt @@ -426,7 +473,7 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error { if err != nil { return err } - return mc.readResultOK() + return mc.resultUnchanged().readResultOK() } default: diff --git a/vendor/github.com/go-sql-driver/mysql/collations.go b/vendor/github.com/go-sql-driver/mysql/collations.go index 295bfbe5..1cdf97b6 100644 --- a/vendor/github.com/go-sql-driver/mysql/collations.go +++ b/vendor/github.com/go-sql-driver/mysql/collations.go @@ -9,7 +9,7 @@ package mysql const defaultCollation = "utf8mb4_general_ci" -const binaryCollation = "binary" +const binaryCollationID = 63 // A list of available collations mapped to the internal ID. // To update this map use the following MySQL query: diff --git a/vendor/github.com/go-sql-driver/mysql/connection.go b/vendor/github.com/go-sql-driver/mysql/connection.go index 9539077c..eff978d9 100644 --- a/vendor/github.com/go-sql-driver/mysql/connection.go +++ b/vendor/github.com/go-sql-driver/mysql/connection.go @@ -23,10 +23,10 @@ import ( type mysqlConn struct { buf buffer netConn net.Conn - rawConn net.Conn // underlying connection when netConn is TLS connection. - affectedRows uint64 - insertId uint64 + rawConn net.Conn // underlying connection when netConn is TLS connection. + result mysqlResult // managed by clearResult() and handleOkPacket(). cfg *Config + connector *connector maxAllowedPacket int maxWriteSize int writeTimeout time.Duration @@ -34,7 +34,6 @@ type mysqlConn struct { status statusFlag sequence uint8 parseTime bool - reset bool // set when the Go SQL package calls ResetSession // for context support (Go 1.8+) watching bool @@ -45,17 +44,27 @@ type mysqlConn struct { closed atomicBool // set when conn is closed, before closech is closed } +// Helper function to call per-connection logger. +func (mc *mysqlConn) log(v ...any) { + mc.cfg.Logger.Print(v...) +} + // Handles parameters set in DSN after the connection is established func (mc *mysqlConn) handleParams() (err error) { var cmdSet strings.Builder + for param, val := range mc.cfg.Params { switch param { // Charset: character_set_connection, character_set_client, character_set_results case "charset": charsets := strings.Split(val, ",") - for i := range charsets { + for _, cs := range charsets { // ignore errors here - a charset may not exist - err = mc.exec("SET NAMES " + charsets[i]) + if mc.cfg.Collation != "" { + err = mc.exec("SET NAMES " + cs + " COLLATE " + mc.cfg.Collation) + } else { + err = mc.exec("SET NAMES " + cs) + } if err == nil { break } @@ -68,13 +77,13 @@ func (mc *mysqlConn) handleParams() (err error) { default: if cmdSet.Len() == 0 { // Heuristic: 29 chars for each other key=value to reduce reallocations - cmdSet.Grow(4 + len(param) + 1 + len(val) + 30*(len(mc.cfg.Params)-1)) + cmdSet.Grow(4 + len(param) + 3 + len(val) + 30*(len(mc.cfg.Params)-1)) cmdSet.WriteString("SET ") } else { - cmdSet.WriteByte(',') + cmdSet.WriteString(", ") } cmdSet.WriteString(param) - cmdSet.WriteByte('=') + cmdSet.WriteString(" = ") cmdSet.WriteString(val) } } @@ -105,7 +114,7 @@ func (mc *mysqlConn) Begin() (driver.Tx, error) { func (mc *mysqlConn) begin(readOnly bool) (driver.Tx, error) { if mc.closed.Load() { - errLog.Print(ErrInvalidConn) + mc.log(ErrInvalidConn) return nil, driver.ErrBadConn } var q string @@ -128,7 +137,7 @@ func (mc *mysqlConn) Close() (err error) { } mc.cleanup() - + mc.clearResult() return } @@ -143,12 +152,16 @@ func (mc *mysqlConn) cleanup() { // Makes cleanup idempotent close(mc.closech) - if mc.netConn == nil { + conn := mc.rawConn + if conn == nil { return } - if err := mc.netConn.Close(); err != nil { - errLog.Print(err) + if err := conn.Close(); err != nil { + mc.log(err) } + // This function can be called from multiple goroutines. + // So we can not mc.clearResult() here. + // Caller should do it if they are in safe goroutine. } func (mc *mysqlConn) error() error { @@ -163,14 +176,14 @@ func (mc *mysqlConn) error() error { func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) { if mc.closed.Load() { - errLog.Print(ErrInvalidConn) + mc.log(ErrInvalidConn) return nil, driver.ErrBadConn } // Send command err := mc.writeCommandPacketStr(comStmtPrepare, query) if err != nil { // STMT_PREPARE is safe to retry. So we can return ErrBadConn here. - errLog.Print(err) + mc.log(err) return nil, driver.ErrBadConn } @@ -204,7 +217,7 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin buf, err := mc.buf.takeCompleteBuffer() if err != nil { // can not take the buffer. Something must be wrong with the connection - errLog.Print(err) + mc.log(err) return "", ErrInvalidConn } buf = buf[:0] @@ -246,7 +259,7 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin buf = append(buf, "'0000-00-00'"...) } else { buf = append(buf, '\'') - buf, err = appendDateTime(buf, v.In(mc.cfg.Loc)) + buf, err = appendDateTime(buf, v.In(mc.cfg.Loc), mc.cfg.timeTruncate) if err != nil { return "", err } @@ -296,7 +309,7 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) { if mc.closed.Load() { - errLog.Print(ErrInvalidConn) + mc.log(ErrInvalidConn) return nil, driver.ErrBadConn } if len(args) != 0 { @@ -310,28 +323,25 @@ func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, err } query = prepared } - mc.affectedRows = 0 - mc.insertId = 0 err := mc.exec(query) if err == nil { - return &mysqlResult{ - affectedRows: int64(mc.affectedRows), - insertId: int64(mc.insertId), - }, err + copied := mc.result + return &copied, err } return nil, mc.markBadConn(err) } // Internal function to execute commands func (mc *mysqlConn) exec(query string) error { + handleOk := mc.clearResult() // Send command if err := mc.writeCommandPacketStr(comQuery, query); err != nil { return mc.markBadConn(err) } // Read Result - resLen, err := mc.readResultSetHeaderPacket() + resLen, err := handleOk.readResultSetHeaderPacket() if err != nil { return err } @@ -348,7 +358,7 @@ func (mc *mysqlConn) exec(query string) error { } } - return mc.discardResults() + return handleOk.discardResults() } func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, error) { @@ -356,8 +366,10 @@ func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, erro } func (mc *mysqlConn) query(query string, args []driver.Value) (*textRows, error) { + handleOk := mc.clearResult() + if mc.closed.Load() { - errLog.Print(ErrInvalidConn) + mc.log(ErrInvalidConn) return nil, driver.ErrBadConn } if len(args) != 0 { @@ -376,7 +388,7 @@ func (mc *mysqlConn) query(query string, args []driver.Value) (*textRows, error) if err == nil { // Read Result var resLen int - resLen, err = mc.readResultSetHeaderPacket() + resLen, err = handleOk.readResultSetHeaderPacket() if err == nil { rows := new(textRows) rows.mc = mc @@ -404,12 +416,13 @@ func (mc *mysqlConn) query(query string, args []driver.Value) (*textRows, error) // The returned byte slice is only valid until the next read func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) { // Send command + handleOk := mc.clearResult() if err := mc.writeCommandPacketStr(comQuery, "SELECT @@"+name); err != nil { return nil, err } // Read Result - resLen, err := mc.readResultSetHeaderPacket() + resLen, err := handleOk.readResultSetHeaderPacket() if err == nil { rows := new(textRows) rows.mc = mc @@ -451,7 +464,7 @@ func (mc *mysqlConn) finish() { // Ping implements driver.Pinger interface func (mc *mysqlConn) Ping(ctx context.Context) (err error) { if mc.closed.Load() { - errLog.Print(ErrInvalidConn) + mc.log(ErrInvalidConn) return driver.ErrBadConn } @@ -460,11 +473,12 @@ func (mc *mysqlConn) Ping(ctx context.Context) (err error) { } defer mc.finish() + handleOk := mc.clearResult() if err = mc.writeCommandPacket(comPing); err != nil { return mc.markBadConn(err) } - return mc.readResultOK() + return handleOk.readResultOK() } // BeginTx implements driver.ConnBeginTx interface @@ -639,7 +653,31 @@ func (mc *mysqlConn) ResetSession(ctx context.Context) error { if mc.closed.Load() { return driver.ErrBadConn } - mc.reset = true + + // Perform a stale connection check. We only perform this check for + // the first query on a connection that has been checked out of the + // connection pool: a fresh connection from the pool is more likely + // to be stale, and it has not performed any previous writes that + // could cause data corruption, so it's safe to return ErrBadConn + // if the check fails. + if mc.cfg.CheckConnLiveness { + conn := mc.netConn + if mc.rawConn != nil { + conn = mc.rawConn + } + var err error + if mc.cfg.ReadTimeout != 0 { + err = conn.SetReadDeadline(time.Now().Add(mc.cfg.ReadTimeout)) + } + if err == nil { + err = connCheck(conn) + } + if err != nil { + mc.log("closing bad idle connection: ", err) + return driver.ErrBadConn + } + } + return nil } diff --git a/vendor/github.com/go-sql-driver/mysql/connector.go b/vendor/github.com/go-sql-driver/mysql/connector.go index d567b4e4..b6707759 100644 --- a/vendor/github.com/go-sql-driver/mysql/connector.go +++ b/vendor/github.com/go-sql-driver/mysql/connector.go @@ -12,10 +12,53 @@ import ( "context" "database/sql/driver" "net" + "os" + "strconv" + "strings" ) type connector struct { - cfg *Config // immutable private copy. + cfg *Config // immutable private copy. + encodedAttributes string // Encoded connection attributes. +} + +func encodeConnectionAttributes(cfg *Config) string { + connAttrsBuf := make([]byte, 0) + + // default connection attributes + connAttrsBuf = appendLengthEncodedString(connAttrsBuf, connAttrClientName) + connAttrsBuf = appendLengthEncodedString(connAttrsBuf, connAttrClientNameValue) + connAttrsBuf = appendLengthEncodedString(connAttrsBuf, connAttrOS) + connAttrsBuf = appendLengthEncodedString(connAttrsBuf, connAttrOSValue) + connAttrsBuf = appendLengthEncodedString(connAttrsBuf, connAttrPlatform) + connAttrsBuf = appendLengthEncodedString(connAttrsBuf, connAttrPlatformValue) + connAttrsBuf = appendLengthEncodedString(connAttrsBuf, connAttrPid) + connAttrsBuf = appendLengthEncodedString(connAttrsBuf, strconv.Itoa(os.Getpid())) + serverHost, _, _ := net.SplitHostPort(cfg.Addr) + if serverHost != "" { + connAttrsBuf = appendLengthEncodedString(connAttrsBuf, connAttrServerHost) + connAttrsBuf = appendLengthEncodedString(connAttrsBuf, serverHost) + } + + // user-defined connection attributes + for _, connAttr := range strings.Split(cfg.ConnectionAttributes, ",") { + k, v, found := strings.Cut(connAttr, ":") + if !found { + continue + } + connAttrsBuf = appendLengthEncodedString(connAttrsBuf, k) + connAttrsBuf = appendLengthEncodedString(connAttrsBuf, v) + } + + return string(connAttrsBuf) +} + +func newConnector(cfg *Config) *connector { + encodedAttributes := encodeConnectionAttributes(cfg) + return &connector{ + cfg: cfg, + encodedAttributes: encodedAttributes, + } } // Connect implements driver.Connector interface. @@ -23,12 +66,23 @@ type connector struct { func (c *connector) Connect(ctx context.Context) (driver.Conn, error) { var err error + // Invoke beforeConnect if present, with a copy of the configuration + cfg := c.cfg + if c.cfg.beforeConnect != nil { + cfg = c.cfg.Clone() + err = c.cfg.beforeConnect(ctx, cfg) + if err != nil { + return nil, err + } + } + // New mysqlConn mc := &mysqlConn{ maxAllowedPacket: maxPacketSize, maxWriteSize: maxPacketSize - 1, closech: make(chan struct{}), - cfg: c.cfg, + cfg: cfg, + connector: c, } mc.parseTime = mc.cfg.ParseTime @@ -48,18 +102,15 @@ func (c *connector) Connect(ctx context.Context) (driver.Conn, error) { nd := net.Dialer{Timeout: mc.cfg.Timeout} mc.netConn, err = nd.DialContext(ctx, mc.cfg.Net, mc.cfg.Addr) } - if err != nil { return nil, err } + mc.rawConn = mc.netConn // Enable TCP Keepalives on TCP connections if tc, ok := mc.netConn.(*net.TCPConn); ok { if err := tc.SetKeepAlive(true); err != nil { - // Don't send COM_QUIT before handshake. - mc.netConn.Close() - mc.netConn = nil - return nil, err + c.cfg.Logger.Print(err) } } @@ -92,7 +143,7 @@ func (c *connector) Connect(ctx context.Context) (driver.Conn, error) { authResp, err := mc.auth(authData, plugin) if err != nil { // try the default auth plugin, if using the requested plugin failed - errLog.Print("could not use requested auth plugin '"+plugin+"': ", err.Error()) + c.cfg.Logger.Print("could not use requested auth plugin '"+plugin+"': ", err.Error()) plugin = defaultAuthPlugin authResp, err = mc.auth(authData, plugin) if err != nil { diff --git a/vendor/github.com/go-sql-driver/mysql/const.go b/vendor/github.com/go-sql-driver/mysql/const.go index b1e6b85e..22526e03 100644 --- a/vendor/github.com/go-sql-driver/mysql/const.go +++ b/vendor/github.com/go-sql-driver/mysql/const.go @@ -8,12 +8,25 @@ package mysql +import "runtime" + const ( defaultAuthPlugin = "mysql_native_password" - defaultMaxAllowedPacket = 4 << 20 // 4 MiB + defaultMaxAllowedPacket = 64 << 20 // 64 MiB. See https://github.com/go-sql-driver/mysql/issues/1355 minProtocolVersion = 10 maxPacketSize = 1<<24 - 1 timeFormat = "2006-01-02 15:04:05.999999" + + // Connection attributes + // See https://dev.mysql.com/doc/refman/8.0/en/performance-schema-connection-attribute-tables.html#performance-schema-connection-attributes-available + connAttrClientName = "_client_name" + connAttrClientNameValue = "Go-MySQL-Driver" + connAttrOS = "_os" + connAttrOSValue = runtime.GOOS + connAttrPlatform = "_platform" + connAttrPlatformValue = runtime.GOARCH + connAttrPid = "_pid" + connAttrServerHost = "_server_host" ) // MySQL constants documentation: diff --git a/vendor/github.com/go-sql-driver/mysql/driver.go b/vendor/github.com/go-sql-driver/mysql/driver.go index ad7aec21..105316b8 100644 --- a/vendor/github.com/go-sql-driver/mysql/driver.go +++ b/vendor/github.com/go-sql-driver/mysql/driver.go @@ -55,6 +55,15 @@ func RegisterDialContext(net string, dial DialContextFunc) { dials[net] = dial } +// DeregisterDialContext removes the custom dial function registered with the given net. +func DeregisterDialContext(net string) { + dialsLock.Lock() + defer dialsLock.Unlock() + if dials != nil { + delete(dials, net) + } +} + // RegisterDial registers a custom dial function. It can then be used by the // network address mynet(addr), where mynet is the registered new network. // addr is passed as a parameter to the dial function. @@ -74,14 +83,18 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) { if err != nil { return nil, err } - c := &connector{ - cfg: cfg, - } + c := newConnector(cfg) return c.Connect(context.Background()) } +// This variable can be replaced with -ldflags like below: +// go build "-ldflags=-X github.com/go-sql-driver/mysql.driverName=custom" +var driverName = "mysql" + func init() { - sql.Register("mysql", &MySQLDriver{}) + if driverName != "" { + sql.Register(driverName, &MySQLDriver{}) + } } // NewConnector returns new driver.Connector. @@ -92,7 +105,7 @@ func NewConnector(cfg *Config) (driver.Connector, error) { if err := cfg.normalize(); err != nil { return nil, err } - return &connector{cfg: cfg}, nil + return newConnector(cfg), nil } // OpenConnector implements driver.DriverContext. @@ -101,7 +114,5 @@ func (d MySQLDriver) OpenConnector(dsn string) (driver.Connector, error) { if err != nil { return nil, err } - return &connector{ - cfg: cfg, - }, nil + return newConnector(cfg), nil } diff --git a/vendor/github.com/go-sql-driver/mysql/dsn.go b/vendor/github.com/go-sql-driver/mysql/dsn.go index 4b71aaab..65f5a024 100644 --- a/vendor/github.com/go-sql-driver/mysql/dsn.go +++ b/vendor/github.com/go-sql-driver/mysql/dsn.go @@ -10,6 +10,7 @@ package mysql import ( "bytes" + "context" "crypto/rsa" "crypto/tls" "errors" @@ -34,22 +35,27 @@ var ( // If a new Config is created instead of being parsed from a DSN string, // the NewConfig function should be used, which sets default values. type Config struct { - User string // Username - Passwd string // Password (requires User) - Net string // Network type - Addr string // Network address (requires Net) - DBName string // Database name - Params map[string]string // Connection parameters - Collation string // Connection collation - Loc *time.Location // Location for time.Time values - MaxAllowedPacket int // Max packet size allowed - ServerPubKey string // Server public key name - pubKey *rsa.PublicKey // Server public key - TLSConfig string // TLS configuration name - TLS *tls.Config // TLS configuration, its priority is higher than TLSConfig - Timeout time.Duration // Dial timeout - ReadTimeout time.Duration // I/O read timeout - WriteTimeout time.Duration // I/O write timeout + // non boolean fields + + User string // Username + Passwd string // Password (requires User) + Net string // Network (e.g. "tcp", "tcp6", "unix". default: "tcp") + Addr string // Address (default: "127.0.0.1:3306" for "tcp" and "/tmp/mysql.sock" for "unix") + DBName string // Database name + Params map[string]string // Connection parameters + ConnectionAttributes string // Connection Attributes, comma-delimited string of user-defined "key:value" pairs + Collation string // Connection collation + Loc *time.Location // Location for time.Time values + MaxAllowedPacket int // Max packet size allowed + ServerPubKey string // Server public key name + TLSConfig string // TLS configuration name + TLS *tls.Config // TLS configuration, its priority is higher than TLSConfig + Timeout time.Duration // Dial timeout + ReadTimeout time.Duration // I/O read timeout + WriteTimeout time.Duration // I/O write timeout + Logger Logger // Logger + + // boolean fields AllowAllFiles bool // Allow all files to be used with LOAD DATA LOCAL INFILE AllowCleartextPasswords bool // Allows the cleartext client side plugin @@ -63,17 +69,57 @@ type Config struct { MultiStatements bool // Allow multiple statements in one query ParseTime bool // Parse time values to time.Time RejectReadOnly bool // Reject read-only connections + + // unexported fields. new options should be come here + + beforeConnect func(context.Context, *Config) error // Invoked before a connection is established + pubKey *rsa.PublicKey // Server public key + timeTruncate time.Duration // Truncate time.Time values to the specified duration } +// Functional Options Pattern +// https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis +type Option func(*Config) error + // NewConfig creates a new Config and sets default values. func NewConfig() *Config { - return &Config{ - Collation: defaultCollation, + cfg := &Config{ Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, + Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true, } + + return cfg +} + +// Apply applies the given options to the Config object. +func (c *Config) Apply(opts ...Option) error { + for _, opt := range opts { + err := opt(c) + if err != nil { + return err + } + } + return nil +} + +// TimeTruncate sets the time duration to truncate time.Time values in +// query parameters. +func TimeTruncate(d time.Duration) Option { + return func(cfg *Config) error { + cfg.timeTruncate = d + return nil + } +} + +// BeforeConnect sets the function to be invoked before a connection is established. +func BeforeConnect(fn func(context.Context, *Config) error) Option { + return func(cfg *Config) error { + cfg.beforeConnect = fn + return nil + } } func (cfg *Config) Clone() *Config { @@ -97,7 +143,7 @@ func (cfg *Config) Clone() *Config { } func (cfg *Config) normalize() error { - if cfg.InterpolateParams && unsafeCollations[cfg.Collation] { + if cfg.InterpolateParams && cfg.Collation != "" && unsafeCollations[cfg.Collation] { return errInvalidDSNUnsafeCollation } @@ -153,6 +199,10 @@ func (cfg *Config) normalize() error { } } + if cfg.Logger == nil { + cfg.Logger = defaultLogger + } + return nil } @@ -171,6 +221,8 @@ func writeDSNParam(buf *bytes.Buffer, hasParam *bool, name, value string) { // FormatDSN formats the given Config into a DSN string which can be passed to // the driver. +// +// Note: use [NewConnector] and [database/sql.OpenDB] to open a connection from a [*Config]. func (cfg *Config) FormatDSN() string { var buf bytes.Buffer @@ -196,7 +248,7 @@ func (cfg *Config) FormatDSN() string { // /dbname buf.WriteByte('/') - buf.WriteString(cfg.DBName) + buf.WriteString(url.PathEscape(cfg.DBName)) // [?param1=value1&...¶mN=valueN] hasParam := false @@ -230,7 +282,7 @@ func (cfg *Config) FormatDSN() string { writeDSNParam(&buf, &hasParam, "clientFoundRows", "true") } - if col := cfg.Collation; col != defaultCollation && len(col) > 0 { + if col := cfg.Collation; col != "" { writeDSNParam(&buf, &hasParam, "collation", col) } @@ -254,6 +306,10 @@ func (cfg *Config) FormatDSN() string { writeDSNParam(&buf, &hasParam, "parseTime", "true") } + if cfg.timeTruncate > 0 { + writeDSNParam(&buf, &hasParam, "timeTruncate", cfg.timeTruncate.String()) + } + if cfg.ReadTimeout > 0 { writeDSNParam(&buf, &hasParam, "readTimeout", cfg.ReadTimeout.String()) } @@ -358,7 +414,11 @@ func ParseDSN(dsn string) (cfg *Config, err error) { break } } - cfg.DBName = dsn[i+1 : j] + + dbname := dsn[i+1 : j] + if cfg.DBName, err = url.PathUnescape(dbname); err != nil { + return nil, fmt.Errorf("invalid dbname %q: %w", dbname, err) + } break } @@ -378,13 +438,13 @@ func ParseDSN(dsn string) (cfg *Config, err error) { // Values must be url.QueryEscape'ed func parseDSNParams(cfg *Config, params string) (err error) { for _, v := range strings.Split(params, "&") { - param := strings.SplitN(v, "=", 2) - if len(param) != 2 { + key, value, found := strings.Cut(v, "=") + if !found { continue } // cfg params - switch value := param[1]; param[0] { + switch key { // Disable INFILE allowlist / enable all files case "allowAllFiles": var isBool bool @@ -490,6 +550,13 @@ func parseDSNParams(cfg *Config, params string) (err error) { return errors.New("invalid bool value: " + value) } + // time.Time truncation + case "timeTruncate": + cfg.timeTruncate, err = time.ParseDuration(value) + if err != nil { + return fmt.Errorf("invalid timeTruncate value: %v, error: %w", value, err) + } + // I/O read Timeout case "readTimeout": cfg.ReadTimeout, err = time.ParseDuration(value) @@ -554,13 +621,22 @@ func parseDSNParams(cfg *Config, params string) (err error) { if err != nil { return } + + // Connection attributes + case "connectionAttributes": + connectionAttributes, err := url.QueryUnescape(value) + if err != nil { + return fmt.Errorf("invalid connectionAttributes value: %v", err) + } + cfg.ConnectionAttributes = connectionAttributes + default: // lazy init if cfg.Params == nil { cfg.Params = make(map[string]string) } - if cfg.Params[param[0]], err = url.QueryUnescape(value); err != nil { + if cfg.Params[key], err = url.QueryUnescape(value); err != nil { return } } diff --git a/vendor/github.com/go-sql-driver/mysql/errors.go b/vendor/github.com/go-sql-driver/mysql/errors.go index 7c037e7d..a7ef8890 100644 --- a/vendor/github.com/go-sql-driver/mysql/errors.go +++ b/vendor/github.com/go-sql-driver/mysql/errors.go @@ -21,13 +21,13 @@ var ( ErrMalformPkt = errors.New("malformed packet") ErrNoTLS = errors.New("TLS requested but server does not support TLS") ErrCleartextPassword = errors.New("this user requires clear text authentication. If you still want to use it, please add 'allowCleartextPasswords=1' to your DSN") - ErrNativePassword = errors.New("this user requires mysql native password authentication.") + ErrNativePassword = errors.New("this user requires mysql native password authentication") ErrOldPassword = errors.New("this user requires old password authentication. If you still want to use it, please add 'allowOldPasswords=1' to your DSN. See also https://github.com/go-sql-driver/mysql/wiki/old_passwords") ErrUnknownPlugin = errors.New("this authentication plugin is not supported") ErrOldProtocol = errors.New("MySQL server does not support required protocol 41+") ErrPktSync = errors.New("commands out of sync. You can't run this command now") ErrPktSyncMul = errors.New("commands out of sync. Did you run multiple statements at once?") - ErrPktTooLarge = errors.New("packet for query is too large. Try adjusting the 'max_allowed_packet' variable on the server") + ErrPktTooLarge = errors.New("packet for query is too large. Try adjusting the `Config.MaxAllowedPacket`") ErrBusyBuffer = errors.New("busy buffer") // errBadConnNoWrite is used for connection errors where nothing was sent to the database yet. @@ -37,20 +37,26 @@ var ( errBadConnNoWrite = errors.New("bad connection") ) -var errLog = Logger(log.New(os.Stderr, "[mysql] ", log.Ldate|log.Ltime|log.Lshortfile)) +var defaultLogger = Logger(log.New(os.Stderr, "[mysql] ", log.Ldate|log.Ltime|log.Lshortfile)) // Logger is used to log critical error messages. type Logger interface { - Print(v ...interface{}) + Print(v ...any) } -// SetLogger is used to set the logger for critical errors. +// NopLogger is a nop implementation of the Logger interface. +type NopLogger struct{} + +// Print implements Logger interface. +func (nl *NopLogger) Print(_ ...any) {} + +// SetLogger is used to set the default logger for critical errors. // The initial logger is os.Stderr. func SetLogger(logger Logger) error { if logger == nil { return errors.New("logger is nil") } - errLog = logger + defaultLogger = logger return nil } diff --git a/vendor/github.com/go-sql-driver/mysql/fields.go b/vendor/github.com/go-sql-driver/mysql/fields.go index e0654a83..28608424 100644 --- a/vendor/github.com/go-sql-driver/mysql/fields.go +++ b/vendor/github.com/go-sql-driver/mysql/fields.go @@ -18,7 +18,7 @@ func (mf *mysqlField) typeDatabaseName() string { case fieldTypeBit: return "BIT" case fieldTypeBLOB: - if mf.charSet != collations[binaryCollation] { + if mf.charSet != binaryCollationID { return "TEXT" } return "BLOB" @@ -37,6 +37,9 @@ func (mf *mysqlField) typeDatabaseName() string { case fieldTypeGeometry: return "GEOMETRY" case fieldTypeInt24: + if mf.flags&flagUnsigned != 0 { + return "UNSIGNED MEDIUMINT" + } return "MEDIUMINT" case fieldTypeJSON: return "JSON" @@ -46,7 +49,7 @@ func (mf *mysqlField) typeDatabaseName() string { } return "INT" case fieldTypeLongBLOB: - if mf.charSet != collations[binaryCollation] { + if mf.charSet != binaryCollationID { return "LONGTEXT" } return "LONGBLOB" @@ -56,7 +59,7 @@ func (mf *mysqlField) typeDatabaseName() string { } return "BIGINT" case fieldTypeMediumBLOB: - if mf.charSet != collations[binaryCollation] { + if mf.charSet != binaryCollationID { return "MEDIUMTEXT" } return "MEDIUMBLOB" @@ -74,7 +77,12 @@ func (mf *mysqlField) typeDatabaseName() string { } return "SMALLINT" case fieldTypeString: - if mf.charSet == collations[binaryCollation] { + if mf.flags&flagEnum != 0 { + return "ENUM" + } else if mf.flags&flagSet != 0 { + return "SET" + } + if mf.charSet == binaryCollationID { return "BINARY" } return "CHAR" @@ -88,17 +96,17 @@ func (mf *mysqlField) typeDatabaseName() string { } return "TINYINT" case fieldTypeTinyBLOB: - if mf.charSet != collations[binaryCollation] { + if mf.charSet != binaryCollationID { return "TINYTEXT" } return "TINYBLOB" case fieldTypeVarChar: - if mf.charSet == collations[binaryCollation] { + if mf.charSet == binaryCollationID { return "VARBINARY" } return "VARCHAR" case fieldTypeVarString: - if mf.charSet == collations[binaryCollation] { + if mf.charSet == binaryCollationID { return "VARBINARY" } return "VARCHAR" @@ -110,21 +118,23 @@ func (mf *mysqlField) typeDatabaseName() string { } var ( - scanTypeFloat32 = reflect.TypeOf(float32(0)) - scanTypeFloat64 = reflect.TypeOf(float64(0)) - scanTypeInt8 = reflect.TypeOf(int8(0)) - scanTypeInt16 = reflect.TypeOf(int16(0)) - scanTypeInt32 = reflect.TypeOf(int32(0)) - scanTypeInt64 = reflect.TypeOf(int64(0)) - scanTypeNullFloat = reflect.TypeOf(sql.NullFloat64{}) - scanTypeNullInt = reflect.TypeOf(sql.NullInt64{}) - scanTypeNullTime = reflect.TypeOf(sql.NullTime{}) - scanTypeUint8 = reflect.TypeOf(uint8(0)) - scanTypeUint16 = reflect.TypeOf(uint16(0)) - scanTypeUint32 = reflect.TypeOf(uint32(0)) - scanTypeUint64 = reflect.TypeOf(uint64(0)) - scanTypeRawBytes = reflect.TypeOf(sql.RawBytes{}) - scanTypeUnknown = reflect.TypeOf(new(interface{})) + scanTypeFloat32 = reflect.TypeOf(float32(0)) + scanTypeFloat64 = reflect.TypeOf(float64(0)) + scanTypeInt8 = reflect.TypeOf(int8(0)) + scanTypeInt16 = reflect.TypeOf(int16(0)) + scanTypeInt32 = reflect.TypeOf(int32(0)) + scanTypeInt64 = reflect.TypeOf(int64(0)) + scanTypeNullFloat = reflect.TypeOf(sql.NullFloat64{}) + scanTypeNullInt = reflect.TypeOf(sql.NullInt64{}) + scanTypeNullTime = reflect.TypeOf(sql.NullTime{}) + scanTypeUint8 = reflect.TypeOf(uint8(0)) + scanTypeUint16 = reflect.TypeOf(uint16(0)) + scanTypeUint32 = reflect.TypeOf(uint32(0)) + scanTypeUint64 = reflect.TypeOf(uint64(0)) + scanTypeString = reflect.TypeOf("") + scanTypeNullString = reflect.TypeOf(sql.NullString{}) + scanTypeBytes = reflect.TypeOf([]byte{}) + scanTypeUnknown = reflect.TypeOf(new(any)) ) type mysqlField struct { @@ -187,12 +197,18 @@ func (mf *mysqlField) scanType() reflect.Type { } return scanTypeNullFloat + case fieldTypeBit, fieldTypeTinyBLOB, fieldTypeMediumBLOB, fieldTypeLongBLOB, + fieldTypeBLOB, fieldTypeVarString, fieldTypeString, fieldTypeGeometry: + if mf.charSet == binaryCollationID { + return scanTypeBytes + } + fallthrough case fieldTypeDecimal, fieldTypeNewDecimal, fieldTypeVarChar, - fieldTypeBit, fieldTypeEnum, fieldTypeSet, fieldTypeTinyBLOB, - fieldTypeMediumBLOB, fieldTypeLongBLOB, fieldTypeBLOB, - fieldTypeVarString, fieldTypeString, fieldTypeGeometry, fieldTypeJSON, - fieldTypeTime: - return scanTypeRawBytes + fieldTypeEnum, fieldTypeSet, fieldTypeJSON, fieldTypeTime: + if mf.flags&flagNotNULL != 0 { + return scanTypeString + } + return scanTypeNullString case fieldTypeDate, fieldTypeNewDate, fieldTypeTimestamp, fieldTypeDateTime: diff --git a/vendor/github.com/go-sql-driver/mysql/fuzz.go b/vendor/github.com/go-sql-driver/mysql/fuzz.go deleted file mode 100644 index 3a4ec25a..00000000 --- a/vendor/github.com/go-sql-driver/mysql/fuzz.go +++ /dev/null @@ -1,25 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package. -// -// Copyright 2020 The Go-MySQL-Driver Authors. All rights reserved. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. - -//go:build gofuzz -// +build gofuzz - -package mysql - -import ( - "database/sql" -) - -func Fuzz(data []byte) int { - db, err := sql.Open("mysql", string(data)) - if err != nil { - return 0 - } - db.Close() - return 1 -} diff --git a/vendor/github.com/go-sql-driver/mysql/infile.go b/vendor/github.com/go-sql-driver/mysql/infile.go index 3279dcff..0c8af9f1 100644 --- a/vendor/github.com/go-sql-driver/mysql/infile.go +++ b/vendor/github.com/go-sql-driver/mysql/infile.go @@ -93,7 +93,7 @@ func deferredClose(err *error, closer io.Closer) { const defaultPacketSize = 16 * 1024 // 16KB is small enough for disk readahead and large enough for TCP -func (mc *mysqlConn) handleInFileRequest(name string) (err error) { +func (mc *okHandler) handleInFileRequest(name string) (err error) { var rdr io.Reader var data []byte packetSize := defaultPacketSize @@ -116,10 +116,10 @@ func (mc *mysqlConn) handleInFileRequest(name string) (err error) { defer deferredClose(&err, cl) } } else { - err = fmt.Errorf("Reader '%s' is ", name) + err = fmt.Errorf("reader '%s' is ", name) } } else { - err = fmt.Errorf("Reader '%s' is not registered", name) + err = fmt.Errorf("reader '%s' is not registered", name) } } else { // File name = strings.Trim(name, `"`) @@ -154,7 +154,7 @@ func (mc *mysqlConn) handleInFileRequest(name string) (err error) { for err == nil { n, err = rdr.Read(data[4:]) if n > 0 { - if ioErr := mc.writePacket(data[:4+n]); ioErr != nil { + if ioErr := mc.conn().writePacket(data[:4+n]); ioErr != nil { return ioErr } } @@ -168,7 +168,7 @@ func (mc *mysqlConn) handleInFileRequest(name string) (err error) { if data == nil { data = make([]byte, 4) } - if ioErr := mc.writePacket(data[:4]); ioErr != nil { + if ioErr := mc.conn().writePacket(data[:4]); ioErr != nil { return ioErr } @@ -177,6 +177,6 @@ func (mc *mysqlConn) handleInFileRequest(name string) (err error) { return mc.readResultOK() } - mc.readPacket() + mc.conn().readPacket() return err } diff --git a/vendor/github.com/go-sql-driver/mysql/nulltime.go b/vendor/github.com/go-sql-driver/mysql/nulltime.go index 36c8a42c..316a48aa 100644 --- a/vendor/github.com/go-sql-driver/mysql/nulltime.go +++ b/vendor/github.com/go-sql-driver/mysql/nulltime.go @@ -38,7 +38,7 @@ type NullTime sql.NullTime // Scan implements the Scanner interface. // The value type must be time.Time or string / []byte (formatted time-string), // otherwise Scan fails. -func (nt *NullTime) Scan(value interface{}) (err error) { +func (nt *NullTime) Scan(value any) (err error) { if value == nil { nt.Time, nt.Valid = time.Time{}, false return @@ -59,7 +59,7 @@ func (nt *NullTime) Scan(value interface{}) (err error) { } nt.Valid = false - return fmt.Errorf("Can't convert %T to time.Time", value) + return fmt.Errorf("can't convert %T to time.Time", value) } // Value implements the driver Valuer interface. diff --git a/vendor/github.com/go-sql-driver/mysql/packets.go b/vendor/github.com/go-sql-driver/mysql/packets.go index ee05c95a..90a34728 100644 --- a/vendor/github.com/go-sql-driver/mysql/packets.go +++ b/vendor/github.com/go-sql-driver/mysql/packets.go @@ -14,10 +14,10 @@ import ( "database/sql/driver" "encoding/binary" "encoding/json" - "errors" "fmt" "io" "math" + "strconv" "time" ) @@ -34,7 +34,7 @@ func (mc *mysqlConn) readPacket() ([]byte, error) { if cerr := mc.canceled.Value(); cerr != nil { return nil, cerr } - errLog.Print(err) + mc.log(err) mc.Close() return nil, ErrInvalidConn } @@ -44,6 +44,7 @@ func (mc *mysqlConn) readPacket() ([]byte, error) { // check packet sync [8 bit] if data[3] != mc.sequence { + mc.Close() if data[3] > mc.sequence { return nil, ErrPktSyncMul } @@ -56,7 +57,7 @@ func (mc *mysqlConn) readPacket() ([]byte, error) { if pktLen == 0 { // there was no previous packet if prevData == nil { - errLog.Print(ErrMalformPkt) + mc.log(ErrMalformPkt) mc.Close() return nil, ErrInvalidConn } @@ -70,7 +71,7 @@ func (mc *mysqlConn) readPacket() ([]byte, error) { if cerr := mc.canceled.Value(); cerr != nil { return nil, cerr } - errLog.Print(err) + mc.log(err) mc.Close() return nil, ErrInvalidConn } @@ -97,34 +98,6 @@ func (mc *mysqlConn) writePacket(data []byte) error { return ErrPktTooLarge } - // Perform a stale connection check. We only perform this check for - // the first query on a connection that has been checked out of the - // connection pool: a fresh connection from the pool is more likely - // to be stale, and it has not performed any previous writes that - // could cause data corruption, so it's safe to return ErrBadConn - // if the check fails. - if mc.reset { - mc.reset = false - conn := mc.netConn - if mc.rawConn != nil { - conn = mc.rawConn - } - var err error - if mc.cfg.CheckConnLiveness { - if mc.cfg.ReadTimeout != 0 { - err = conn.SetReadDeadline(time.Now().Add(mc.cfg.ReadTimeout)) - } - if err == nil { - err = connCheck(conn) - } - } - if err != nil { - errLog.Print("closing bad idle connection: ", err) - mc.Close() - return driver.ErrBadConn - } - } - for { var size int if pktLen >= maxPacketSize { @@ -161,7 +134,7 @@ func (mc *mysqlConn) writePacket(data []byte) error { // Handle error if err == nil { // n != len(data) mc.cleanup() - errLog.Print(ErrMalformPkt) + mc.log(ErrMalformPkt) } else { if cerr := mc.canceled.Value(); cerr != nil { return cerr @@ -171,7 +144,7 @@ func (mc *mysqlConn) writePacket(data []byte) error { return errBadConnNoWrite } mc.cleanup() - errLog.Print(err) + mc.log(err) } return ErrInvalidConn } @@ -239,7 +212,7 @@ func (mc *mysqlConn) readHandshakePacket() (data []byte, plugin string, err erro // reserved (all [00]) [10 bytes] pos += 1 + 2 + 2 + 1 + 10 - // second part of the password cipher [mininum 13 bytes], + // second part of the password cipher [minimum 13 bytes], // where len=MAX(13, length of auth-plugin-data - 8) // // The web documentation is ambiguous about the length. However, @@ -285,6 +258,7 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string clientLocalFiles | clientPluginAuth | clientMultiResults | + clientConnectAttrs | mc.flags&clientLongFlag if mc.cfg.ClientFoundRows { @@ -318,11 +292,17 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string pktLen += n + 1 } + // encode length of the connection attributes + var connAttrsLEIBuf [9]byte + connAttrsLen := len(mc.connector.encodedAttributes) + connAttrsLEI := appendLengthEncodedInteger(connAttrsLEIBuf[:0], uint64(connAttrsLen)) + pktLen += len(connAttrsLEI) + len(mc.connector.encodedAttributes) + // Calculate packet length and get buffer with that size - data, err := mc.buf.takeSmallBuffer(pktLen + 4) + data, err := mc.buf.takeBuffer(pktLen + 4) if err != nil { // cannot take the buffer. Something must be wrong with the connection - errLog.Print(err) + mc.log(err) return errBadConnNoWrite } @@ -338,14 +318,18 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string data[10] = 0x00 data[11] = 0x00 - // Charset [1 byte] + // Collation ID [1 byte] + cname := mc.cfg.Collation + if cname == "" { + cname = defaultCollation + } var found bool - data[12], found = collations[mc.cfg.Collation] + data[12], found = collations[cname] if !found { // Note possibility for false negatives: // could be triggered although the collation is valid if the // collations map does not contain entries the server supports. - return errors.New("unknown collation") + return fmt.Errorf("unknown collation: %q", cname) } // Filler [23 bytes] (all 0x00) @@ -367,7 +351,6 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string if err := tlsConn.Handshake(); err != nil { return err } - mc.rawConn = mc.netConn mc.netConn = tlsConn mc.buf.nc = tlsConn } @@ -394,6 +377,10 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string data[pos] = 0x00 pos++ + // Connection Attributes + pos += copy(data[pos:], connAttrsLEI) + pos += copy(data[pos:], []byte(mc.connector.encodedAttributes)) + // Send Auth packet return mc.writePacket(data[:pos]) } @@ -404,7 +391,7 @@ func (mc *mysqlConn) writeAuthSwitchPacket(authData []byte) error { data, err := mc.buf.takeSmallBuffer(pktLen) if err != nil { // cannot take the buffer. Something must be wrong with the connection - errLog.Print(err) + mc.log(err) return errBadConnNoWrite } @@ -424,7 +411,7 @@ func (mc *mysqlConn) writeCommandPacket(command byte) error { data, err := mc.buf.takeSmallBuffer(4 + 1) if err != nil { // cannot take the buffer. Something must be wrong with the connection - errLog.Print(err) + mc.log(err) return errBadConnNoWrite } @@ -443,7 +430,7 @@ func (mc *mysqlConn) writeCommandPacketStr(command byte, arg string) error { data, err := mc.buf.takeBuffer(pktLen + 4) if err != nil { // cannot take the buffer. Something must be wrong with the connection - errLog.Print(err) + mc.log(err) return errBadConnNoWrite } @@ -464,7 +451,7 @@ func (mc *mysqlConn) writeCommandPacketUint32(command byte, arg uint32) error { data, err := mc.buf.takeSmallBuffer(4 + 1 + 4) if err != nil { // cannot take the buffer. Something must be wrong with the connection - errLog.Print(err) + mc.log(err) return errBadConnNoWrite } @@ -495,7 +482,9 @@ func (mc *mysqlConn) readAuthResult() ([]byte, string, error) { switch data[0] { case iOK: - return nil, "", mc.handleOkPacket(data) + // resultUnchanged, since auth happens before any queries or + // commands have been executed. + return nil, "", mc.resultUnchanged().handleOkPacket(data) case iAuthMoreData: return data[1:], "", err @@ -518,9 +507,9 @@ func (mc *mysqlConn) readAuthResult() ([]byte, string, error) { } } -// Returns error if Packet is not an 'Result OK'-Packet -func (mc *mysqlConn) readResultOK() error { - data, err := mc.readPacket() +// Returns error if Packet is not a 'Result OK'-Packet +func (mc *okHandler) readResultOK() error { + data, err := mc.conn().readPacket() if err != nil { return err } @@ -528,13 +517,17 @@ func (mc *mysqlConn) readResultOK() error { if data[0] == iOK { return mc.handleOkPacket(data) } - return mc.handleErrorPacket(data) + return mc.conn().handleErrorPacket(data) } // Result Set Header Packet // http://dev.mysql.com/doc/internals/en/com-query-response.html#packet-ProtocolText::Resultset -func (mc *mysqlConn) readResultSetHeaderPacket() (int, error) { - data, err := mc.readPacket() +func (mc *okHandler) readResultSetHeaderPacket() (int, error) { + // handleOkPacket replaces both values; other cases leave the values unchanged. + mc.result.affectedRows = append(mc.result.affectedRows, 0) + mc.result.insertIds = append(mc.result.insertIds, 0) + + data, err := mc.conn().readPacket() if err == nil { switch data[0] { @@ -542,19 +535,16 @@ func (mc *mysqlConn) readResultSetHeaderPacket() (int, error) { return 0, mc.handleOkPacket(data) case iERR: - return 0, mc.handleErrorPacket(data) + return 0, mc.conn().handleErrorPacket(data) case iLocalInFile: return 0, mc.handleInFileRequest(string(data[1:])) } // column count - num, _, n := readLengthEncodedInteger(data) - if n-len(data) == 0 { - return int(num), nil - } - - return 0, ErrMalformPkt + num, _, _ := readLengthEncodedInteger(data) + // ignore remaining data in the packet. see #1478. + return int(num), nil } return 0, err } @@ -607,18 +597,61 @@ func readStatus(b []byte) statusFlag { return statusFlag(b[0]) | statusFlag(b[1])<<8 } +// Returns an instance of okHandler for codepaths where mysqlConn.result doesn't +// need to be cleared first (e.g. during authentication, or while additional +// resultsets are being fetched.) +func (mc *mysqlConn) resultUnchanged() *okHandler { + return (*okHandler)(mc) +} + +// okHandler represents the state of the connection when mysqlConn.result has +// been prepared for processing of OK packets. +// +// To correctly populate mysqlConn.result (updated by handleOkPacket()), all +// callpaths must either: +// +// 1. first clear it using clearResult(), or +// 2. confirm that they don't need to (by calling resultUnchanged()). +// +// Both return an instance of type *okHandler. +type okHandler mysqlConn + +// Exposes the underlying type's methods. +func (mc *okHandler) conn() *mysqlConn { + return (*mysqlConn)(mc) +} + +// clearResult clears the connection's stored affectedRows and insertIds +// fields. +// +// It returns a handler that can process OK responses. +func (mc *mysqlConn) clearResult() *okHandler { + mc.result = mysqlResult{} + return (*okHandler)(mc) +} + // Ok Packet // http://dev.mysql.com/doc/internals/en/generic-response-packets.html#packet-OK_Packet -func (mc *mysqlConn) handleOkPacket(data []byte) error { +func (mc *okHandler) handleOkPacket(data []byte) error { var n, m int + var affectedRows, insertId uint64 // 0x00 [1 byte] // Affected rows [Length Coded Binary] - mc.affectedRows, _, n = readLengthEncodedInteger(data[1:]) + affectedRows, _, n = readLengthEncodedInteger(data[1:]) // Insert id [Length Coded Binary] - mc.insertId, _, m = readLengthEncodedInteger(data[1+n:]) + insertId, _, m = readLengthEncodedInteger(data[1+n:]) + + // Update for the current statement result (only used by + // readResultSetHeaderPacket). + if len(mc.result.affectedRows) > 0 { + mc.result.affectedRows[len(mc.result.affectedRows)-1] = int64(affectedRows) + } + if len(mc.result.insertIds) > 0 { + mc.result.insertIds[len(mc.result.insertIds)-1] = int64(insertId) + } // server_status [2 bytes] mc.status = readStatus(data[1+n+m : 1+n+m+2]) @@ -769,7 +802,8 @@ func (rows *textRows) readRow(dest []driver.Value) error { for i := range dest { // Read bytes and convert to string - dest[i], isNull, n, err = readLengthEncodedString(data[pos:]) + var buf []byte + buf, isNull, n, err = readLengthEncodedString(data[pos:]) pos += n if err != nil { @@ -781,19 +815,40 @@ func (rows *textRows) readRow(dest []driver.Value) error { continue } - if !mc.parseTime { - continue - } - - // Parse time field switch rows.rs.columns[i].fieldType { case fieldTypeTimestamp, fieldTypeDateTime, fieldTypeDate, fieldTypeNewDate: - if dest[i], err = parseDateTime(dest[i].([]byte), mc.cfg.Loc); err != nil { - return err + if mc.parseTime { + dest[i], err = parseDateTime(buf, mc.cfg.Loc) + } else { + dest[i] = buf } + + case fieldTypeTiny, fieldTypeShort, fieldTypeInt24, fieldTypeYear, fieldTypeLong: + dest[i], err = strconv.ParseInt(string(buf), 10, 64) + + case fieldTypeLongLong: + if rows.rs.columns[i].flags&flagUnsigned != 0 { + dest[i], err = strconv.ParseUint(string(buf), 10, 64) + } else { + dest[i], err = strconv.ParseInt(string(buf), 10, 64) + } + + case fieldTypeFloat: + var d float64 + d, err = strconv.ParseFloat(string(buf), 32) + dest[i] = float32(d) + + case fieldTypeDouble: + dest[i], err = strconv.ParseFloat(string(buf), 64) + + default: + dest[i] = buf + } + if err != nil { + return err } } @@ -938,7 +993,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error { } if err != nil { // cannot take the buffer. Something must be wrong with the connection - errLog.Print(err) + mc.log(err) return errBadConnNoWrite } @@ -1116,7 +1171,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error { if v.IsZero() { b = append(b, "0000-00-00"...) } else { - b, err = appendDateTime(b, v.In(mc.cfg.Loc)) + b, err = appendDateTime(b, v.In(mc.cfg.Loc), mc.cfg.timeTruncate) if err != nil { return err } @@ -1137,7 +1192,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error { if valuesCap != cap(paramValues) { data = append(data[:pos], paramValues...) if err = mc.buf.store(data); err != nil { - errLog.Print(err) + mc.log(err) return errBadConnNoWrite } } @@ -1149,7 +1204,9 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error { return mc.writePacket(data) } -func (mc *mysqlConn) discardResults() error { +// For each remaining resultset in the stream, discards its rows and updates +// mc.affectedRows and mc.insertIds. +func (mc *okHandler) discardResults() error { for mc.status&statusMoreResultsExists != 0 { resLen, err := mc.readResultSetHeaderPacket() if err != nil { @@ -1157,11 +1214,11 @@ func (mc *mysqlConn) discardResults() error { } if resLen > 0 { // columns - if err := mc.readUntilEOF(); err != nil { + if err := mc.conn().readUntilEOF(); err != nil { return err } // rows - if err := mc.readUntilEOF(); err != nil { + if err := mc.conn().readUntilEOF(); err != nil { return err } } diff --git a/vendor/github.com/go-sql-driver/mysql/result.go b/vendor/github.com/go-sql-driver/mysql/result.go index c6438d03..d5163146 100644 --- a/vendor/github.com/go-sql-driver/mysql/result.go +++ b/vendor/github.com/go-sql-driver/mysql/result.go @@ -8,15 +8,43 @@ package mysql +import "database/sql/driver" + +// Result exposes data not available through *connection.Result. +// +// This is accessible by executing statements using sql.Conn.Raw() and +// downcasting the returned result: +// +// res, err := rawConn.Exec(...) +// res.(mysql.Result).AllRowsAffected() +type Result interface { + driver.Result + // AllRowsAffected returns a slice containing the affected rows for each + // executed statement. + AllRowsAffected() []int64 + // AllLastInsertIds returns a slice containing the last inserted ID for each + // executed statement. + AllLastInsertIds() []int64 +} + type mysqlResult struct { - affectedRows int64 - insertId int64 + // One entry in both slices is created for every executed statement result. + affectedRows []int64 + insertIds []int64 } func (res *mysqlResult) LastInsertId() (int64, error) { - return res.insertId, nil + return res.insertIds[len(res.insertIds)-1], nil } func (res *mysqlResult) RowsAffected() (int64, error) { - return res.affectedRows, nil + return res.affectedRows[len(res.affectedRows)-1], nil +} + +func (res *mysqlResult) AllLastInsertIds() []int64 { + return append([]int64{}, res.insertIds...) // defensive copy +} + +func (res *mysqlResult) AllRowsAffected() []int64 { + return append([]int64{}, res.affectedRows...) // defensive copy } diff --git a/vendor/github.com/go-sql-driver/mysql/rows.go b/vendor/github.com/go-sql-driver/mysql/rows.go index 888bdb5f..81fa6062 100644 --- a/vendor/github.com/go-sql-driver/mysql/rows.go +++ b/vendor/github.com/go-sql-driver/mysql/rows.go @@ -123,7 +123,8 @@ func (rows *mysqlRows) Close() (err error) { err = mc.readUntilEOF() } if err == nil { - if err = mc.discardResults(); err != nil { + handleOk := mc.clearResult() + if err = handleOk.discardResults(); err != nil { return err } } @@ -160,7 +161,15 @@ func (rows *mysqlRows) nextResultSet() (int, error) { return 0, io.EOF } rows.rs = resultSet{} - return rows.mc.readResultSetHeaderPacket() + // rows.mc.affectedRows and rows.mc.insertIds accumulate on each call to + // nextResultSet. + resLen, err := rows.mc.resultUnchanged().readResultSetHeaderPacket() + if err != nil { + // Clean up about multi-results flag + rows.rs.done = true + rows.mc.status = rows.mc.status & (^statusMoreResultsExists) + } + return resLen, err } func (rows *mysqlRows) nextNotEmptyResultSet() (int, error) { diff --git a/vendor/github.com/go-sql-driver/mysql/statement.go b/vendor/github.com/go-sql-driver/mysql/statement.go index 10ece8bd..0436f224 100644 --- a/vendor/github.com/go-sql-driver/mysql/statement.go +++ b/vendor/github.com/go-sql-driver/mysql/statement.go @@ -51,7 +51,7 @@ func (stmt *mysqlStmt) CheckNamedValue(nv *driver.NamedValue) (err error) { func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) { if stmt.mc.closed.Load() { - errLog.Print(ErrInvalidConn) + stmt.mc.log(ErrInvalidConn) return nil, driver.ErrBadConn } // Send command @@ -61,12 +61,10 @@ func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) { } mc := stmt.mc - - mc.affectedRows = 0 - mc.insertId = 0 + handleOk := stmt.mc.clearResult() // Read Result - resLen, err := mc.readResultSetHeaderPacket() + resLen, err := handleOk.readResultSetHeaderPacket() if err != nil { return nil, err } @@ -83,14 +81,12 @@ func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) { } } - if err := mc.discardResults(); err != nil { + if err := handleOk.discardResults(); err != nil { return nil, err } - return &mysqlResult{ - affectedRows: int64(mc.affectedRows), - insertId: int64(mc.insertId), - }, nil + copied := mc.result + return &copied, nil } func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) { @@ -99,7 +95,7 @@ func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) { func (stmt *mysqlStmt) query(args []driver.Value) (*binaryRows, error) { if stmt.mc.closed.Load() { - errLog.Print(ErrInvalidConn) + stmt.mc.log(ErrInvalidConn) return nil, driver.ErrBadConn } // Send command @@ -111,7 +107,8 @@ func (stmt *mysqlStmt) query(args []driver.Value) (*binaryRows, error) { mc := stmt.mc // Read Result - resLen, err := mc.readResultSetHeaderPacket() + handleOk := stmt.mc.clearResult() + resLen, err := handleOk.readResultSetHeaderPacket() if err != nil { return nil, err } @@ -144,7 +141,7 @@ type converter struct{} // implementation does not. This function should be kept in sync with // database/sql/driver defaultConverter.ConvertValue() except for that // deliberate difference. -func (c converter) ConvertValue(v interface{}) (driver.Value, error) { +func (c converter) ConvertValue(v any) (driver.Value, error) { if driver.IsValue(v) { return v, nil } diff --git a/vendor/github.com/go-sql-driver/mysql/utils.go b/vendor/github.com/go-sql-driver/mysql/utils.go index 15dbd8d1..cda24fe7 100644 --- a/vendor/github.com/go-sql-driver/mysql/utils.go +++ b/vendor/github.com/go-sql-driver/mysql/utils.go @@ -36,7 +36,7 @@ var ( // registering it. // // rootCertPool := x509.NewCertPool() -// pem, err := ioutil.ReadFile("/path/ca-cert.pem") +// pem, err := os.ReadFile("/path/ca-cert.pem") // if err != nil { // log.Fatal(err) // } @@ -265,7 +265,11 @@ func parseBinaryDateTime(num uint64, data []byte, loc *time.Location) (driver.Va return nil, fmt.Errorf("invalid DATETIME packet length %d", num) } -func appendDateTime(buf []byte, t time.Time) ([]byte, error) { +func appendDateTime(buf []byte, t time.Time, timeTruncate time.Duration) ([]byte, error) { + if timeTruncate > 0 { + t = t.Truncate(timeTruncate) + } + year, month, day := t.Date() hour, min, sec := t.Clock() nsec := t.Nanosecond() @@ -616,6 +620,11 @@ func appendLengthEncodedInteger(b []byte, n uint64) []byte { byte(n>>32), byte(n>>40), byte(n>>48), byte(n>>56)) } +func appendLengthEncodedString(b []byte, s string) []byte { + b = appendLengthEncodedInteger(b, uint64(len(s))) + return append(b, s...) +} + // reserveBuffer checks cap(buf) and expand buffer to len(buf) + appendSize. // If cap(buf) is not enough, reallocate new buffer. func reserveBuffer(buf []byte, appendSize int) []byte { diff --git a/vendor/gorm.io/driver/mysql/migrator.go b/vendor/gorm.io/driver/mysql/migrator.go index fcdb63c5..e266cfdc 100644 --- a/vendor/gorm.io/driver/mysql/migrator.go +++ b/vendor/gorm.io/driver/mysql/migrator.go @@ -155,7 +155,7 @@ func (m Migrator) AlterColumn(value interface{}, field string) error { return m.DB.Exec( "ALTER TABLE ? MODIFY COLUMN ? ?", - clause.Table{Name: stmt.Table}, clause.Column{Name: field.DBName}, fullDataType, + m.CurrentTable(stmt), clause.Column{Name: field.DBName}, fullDataType, ).Error } } @@ -215,7 +215,7 @@ func (m Migrator) RenameColumn(value interface{}, oldName, newName string) error if field != nil { return m.DB.Exec( "ALTER TABLE ? CHANGE ? ? ?", - clause.Table{Name: stmt.Table}, clause.Column{Name: oldName}, + m.CurrentTable(stmt), clause.Column{Name: oldName}, clause.Column{Name: newName}, m.FullDataTypeOf(field), ).Error } @@ -252,7 +252,7 @@ func (m Migrator) RenameIndex(value interface{}, oldName, newName string) error return m.RunWithValue(value, func(stmt *gorm.Statement) error { return m.DB.Exec( "ALTER TABLE ? RENAME INDEX ? TO ?", - clause.Table{Name: stmt.Table}, clause.Column{Name: oldName}, clause.Column{Name: newName}, + m.CurrentTable(stmt), clause.Column{Name: oldName}, clause.Column{Name: newName}, ).Error }) } @@ -267,7 +267,7 @@ func (m Migrator) RenameIndex(value interface{}, oldName, newName string) error if idx := stmt.Schema.LookIndex(newName); idx == nil { if idx = stmt.Schema.LookIndex(oldName); idx != nil { opts := m.BuildIndexOptions(idx.Fields, stmt) - values := []interface{}{clause.Column{Name: newName}, clause.Table{Name: stmt.Table}, opts} + values := []interface{}{clause.Column{Name: newName}, m.CurrentTable(stmt), opts} createIndexSQL := "CREATE " if idx.Class != "" { @@ -295,7 +295,7 @@ func (m Migrator) DropTable(values ...interface{}) error { tx.Exec("SET FOREIGN_KEY_CHECKS = 0;") for i := len(values) - 1; i >= 0; i-- { if err := m.RunWithValue(values[i], func(stmt *gorm.Statement) error { - return tx.Exec("DROP TABLE IF EXISTS ? CASCADE", clause.Table{Name: stmt.Table}).Error + return tx.Exec("DROP TABLE IF EXISTS ? CASCADE", m.CurrentTable(stmt)).Error }); err != nil { return err } diff --git a/vendor/gorm.io/driver/mysql/mysql.go b/vendor/gorm.io/driver/mysql/mysql.go index bdef77bb..8b6ef3db 100644 --- a/vendor/gorm.io/driver/mysql/mysql.go +++ b/vendor/gorm.io/driver/mysql/mysql.go @@ -22,6 +22,8 @@ import ( ) const ( + DefaultDriverName = "mysql" + AutoRandomTag = "auto_random()" // Treated as an auto_random field for tidb ) @@ -80,7 +82,7 @@ func New(config Config) gorm.Dialector { } func (dialector Dialector) Name() string { - return "mysql" + return DefaultDriverName } // NowFunc return now func @@ -107,7 +109,7 @@ func (dialector Dialector) Apply(config *gorm.Config) error { func (dialector Dialector) Initialize(db *gorm.DB) (err error) { if dialector.DriverName == "" { - dialector.DriverName = "mysql" + dialector.DriverName = DefaultDriverName } if dialector.DefaultDatetimePrecision == nil { diff --git a/vendor/modules.txt b/vendor/modules.txt index 6007bc17..a7d225a8 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,3 +1,7 @@ +# filippo.io/edwards25519 v1.1.0 +## explicit; go 1.20 +filippo.io/edwards25519 +filippo.io/edwards25519/field # github.com/NathanBaulch/protoc-gen-cobra v1.2.1 ## explicit; go 1.18 github.com/NathanBaulch/protoc-gen-cobra/client @@ -5,7 +9,7 @@ github.com/NathanBaulch/protoc-gen-cobra/flag github.com/NathanBaulch/protoc-gen-cobra/iocodec github.com/NathanBaulch/protoc-gen-cobra/naming github.com/NathanBaulch/protoc-gen-cobra/ptypes -# github.com/PaulSonOfLars/gotgbot/v2 v2.0.0-rc.26 +# github.com/PaulSonOfLars/gotgbot/v2 v2.0.0-rc.27 ## explicit; go 1.19 github.com/PaulSonOfLars/gotgbot/v2 github.com/PaulSonOfLars/gotgbot/v2/ext @@ -18,8 +22,8 @@ github.com/davecgh/go-spew/spew # github.com/fxamacker/cbor/v2 v2.7.0 ## explicit; go 1.17 github.com/fxamacker/cbor/v2 -# github.com/go-sql-driver/mysql v1.7.0 -## explicit; go 1.13 +# github.com/go-sql-driver/mysql v1.8.1 +## explicit; go 1.18 github.com/go-sql-driver/mysql # github.com/google/pprof v0.0.0-20240622144329-c177fd99eaa9 ## explicit; go 1.19 @@ -173,10 +177,10 @@ golang.org/x/text/secure/bidirule golang.org/x/text/transform golang.org/x/text/unicode/bidi golang.org/x/text/unicode/norm -# google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d +# google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 ## explicit; go 1.20 google.golang.org/genproto/googleapis/api/httpbody -# google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d +# google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 ## explicit; go 1.20 google.golang.org/genproto/googleapis/rpc/status # google.golang.org/grpc v1.64.0 @@ -276,7 +280,7 @@ gopkg.in/natefinch/lumberjack.v2 # gopkg.in/yaml.v3 v3.0.1 ## explicit gopkg.in/yaml.v3 -# gorm.io/driver/mysql v1.5.6 +# gorm.io/driver/mysql v1.5.7 ## explicit; go 1.14 gorm.io/driver/mysql # gorm.io/gorm v1.25.10 From d9cb3a2afda951ccfaa813c7d407f7124d75fe96 Mon Sep 17 00:00:00 2001 From: mj Date: Tue, 2 Jul 2024 13:18:39 +0330 Subject: [PATCH 2/7] fix(voucher): voucher creation command --- .../command/{user.go => user.middleware.go} | 0 internal/engine/command/user_test.go | 27 -------------- internal/engine/command/voucher/claim.go | 4 +- internal/engine/command/voucher/create.go | 21 +++++------ internal/engine/command/voucher/voucher.go | 32 +++++----------- .../{wallet.go => wallet.middleware.go} | 2 +- internal/entity/voucher.go | 4 +- internal/repository/voucher.go | 4 +- pkg/utils/random.go | 37 +++++++++++++++++++ 9 files changed, 63 insertions(+), 68 deletions(-) rename internal/engine/command/{user.go => user.middleware.go} (100%) delete mode 100644 internal/engine/command/user_test.go rename internal/engine/command/{wallet.go => wallet.middleware.go} (82%) create mode 100644 pkg/utils/random.go diff --git a/internal/engine/command/user.go b/internal/engine/command/user.middleware.go similarity index 100% rename from internal/engine/command/user.go rename to internal/engine/command/user.middleware.go diff --git a/internal/engine/command/user_test.go b/internal/engine/command/user_test.go deleted file mode 100644 index 5471aaef..00000000 --- a/internal/engine/command/user_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package command - -import ( - "testing" - - "github.com/pagu-project/Pagu/internal/entity" - "github.com/pagu-project/Pagu/internal/repository" - "github.com/stretchr/testify/assert" -) - -func setup() *repository.DB { - // dbPath := os.Getenv("DB") - // db, _ := repository.NewDB(dbPath) - db, _ := repository.NewDB("root:ns1294password@tcp(127.0.0.1:4417)/pagu?parseTime=true") - return db -} - -func TestMiddlewareHandler_CreateUser(t *testing.T) { - db := setup() - middlewareHandler := NewMiddlewareHandler(db, nil) - cmd := &Command{} - - t.Run("success creation", func(t *testing.T) { - err := middlewareHandler.CreateUser(cmd, entity.AppIdDiscord, "ABCD") - assert.Equal(t, nil, err) - }) -} diff --git a/internal/engine/command/voucher/claim.go b/internal/engine/command/voucher/claim.go index 05bf0fe1..336560be 100644 --- a/internal/engine/command/voucher/claim.go +++ b/internal/engine/command/voucher/claim.go @@ -8,7 +8,7 @@ import ( "github.com/pagu-project/Pagu/internal/entity" ) -func (v *Voucher) claimHandler(cmd command.Command, _ entity.AppID, _ string, args ...string) command.CommandResult { +func (v *Voucher) claimHandler(cmd command.Command, _ entity.AppID, callerID string, args ...string) command.CommandResult { code := args[0] if len(code) != 8 { return cmd.ErrorResult(errors.New("voucher code is not valid")) @@ -44,7 +44,7 @@ func (v *Voucher) claimHandler(cmd command.Command, _ entity.AppID, _ string, ar return cmd.ErrorResult(errors.New("can't send bond transaction")) } - if err = v.db.UpdateVoucherTx(voucher.ID, txHash); err != nil { + if err = v.db.UpdateVoucher(voucher.ID, txHash, callerID); err != nil { return cmd.ErrorResult(err) } diff --git a/internal/engine/command/voucher/create.go b/internal/engine/command/voucher/create.go index 9942f393..6b33ac90 100644 --- a/internal/engine/command/voucher/create.go +++ b/internal/engine/command/voucher/create.go @@ -3,24 +3,24 @@ package voucher import ( "strconv" + "github.com/pagu-project/Pagu/pkg/utils" + "github.com/pagu-project/Pagu/internal/engine/command" "github.com/pagu-project/Pagu/internal/entity" ) -func (v *Voucher) createHandler(cmd command.Command, _ entity.AppID, callerID string, args ...string) command.CommandResult { +func (v *Voucher) createHandler(cmd command.Command, _ entity.AppID, _ string, args ...string) command.CommandResult { //! Admin only check - cID, err := strconv.Atoi(callerID) - if err != nil { - return cmd.ErrorResult(err) + code := utils.RandomString(8, utils.CapitalLetterNumbers) + for _, err := v.db.GetVoucherByCode(code); err == nil; { + code = utils.RandomString(8, utils.CapitalLetterNumbers) } recipient := args[0] - description := args[1] + amount := args[1] validMonths := args[2] - amount := args[3] - discordID := args[4] - code := args[5] + description := args[3] expireMonths, err := strconv.Atoi(validMonths) if err != nil { @@ -33,12 +33,11 @@ func (v *Voucher) createHandler(cmd command.Command, _ entity.AppID, callerID st } err = v.db.AddVoucher(&entity.Voucher{ - Creator: uint(cID), + Creator: cmd.User.ID, Code: code, Desc: description, - DiscordID: discordID, Recipient: recipient, - ValidMonths: uint(expireMonths), + ValidMonths: uint8(expireMonths), Amount: uint(intAmount), }) if err != nil { diff --git a/internal/engine/command/voucher/voucher.go b/internal/engine/command/voucher/voucher.go index 4eb5e1a9..7ee24438 100644 --- a/internal/engine/command/voucher/voucher.go +++ b/internal/engine/command/voucher/voucher.go @@ -30,6 +30,8 @@ func NewVoucher(db *repository.DB, wallet *wallet.Wallet, cli *client.Mgr) Vouch } func (v *Voucher) GetCommand() command.Command { + middlewareHandler := command.NewMiddlewareHandler(v.db, v.wallet) + subCmdClaim := command.Command{ Name: ClaimCommandName, Help: "Claim your voucher coins and bond to validator", @@ -47,25 +49,17 @@ func (v *Voucher) GetCommand() command.Command { }, SubCommands: nil, AppIDs: entity.AllAppIDs(), + Middlewares: []command.MiddlewareFunc{middlewareHandler.CreateUser, middlewareHandler.WalletBalance}, Handler: v.claimHandler, + TargetFlag: command.TargetMaskMain, } subCmdCreate := command.Command{ - Name: ClaimCommandName, + Name: CreateCommandName, Help: "Add a new voucher to database", Args: []command.Args{ { - Name: "recipient", - Desc: "Indicates the name of the recipient of the voucher", - Optional: false, - }, - { - Name: "description", - Desc: "Describes the reason for issuing the voucher", - Optional: false, - }, - { - Name: "valid-months", + Name: "valid-days", Desc: "Indicates how many months the voucher is valid after it is issued", Optional: false, }, @@ -74,20 +68,12 @@ func (v *Voucher) GetCommand() command.Command { Desc: "Amount of PAC to bond", Optional: false, }, - { - Name: "discord-id", - Desc: "Recipient Discord ID", - Optional: false, - }, - { - Name: "code", - Desc: "The voucher code", - Optional: false, - }, }, SubCommands: nil, AppIDs: entity.AllAppIDs(), + Middlewares: []command.MiddlewareFunc{middlewareHandler.CreateUser}, Handler: v.createHandler, + TargetFlag: command.TargetMaskModerator, } cmdVoucher := command.Command{ @@ -97,7 +83,7 @@ func (v *Voucher) GetCommand() command.Command { AppIDs: entity.AllAppIDs(), SubCommands: make([]command.Command, 0), Handler: nil, - TargetFlag: command.TargetMaskMain, + TargetFlag: command.TargetMaskMain | command.TargetMaskModerator, } cmdVoucher.AddSubCommand(subCmdClaim) diff --git a/internal/engine/command/wallet.go b/internal/engine/command/wallet.middleware.go similarity index 82% rename from internal/engine/command/wallet.go rename to internal/engine/command/wallet.middleware.go index 678fc5ac..1576897d 100644 --- a/internal/engine/command/wallet.go +++ b/internal/engine/command/wallet.middleware.go @@ -8,7 +8,7 @@ import ( func (h *MiddlewareHandler) WalletBalance(_ *Command, _ entity.AppID, _ string, _ ...string) error { if h.wallet.Balance() < 5 { - return errors.New("empty pagu phoenix wallet") + return errors.New("empty pagu wallet balance") } return nil diff --git a/internal/entity/voucher.go b/internal/entity/voucher.go index 15f8f8da..92b034d4 100644 --- a/internal/entity/voucher.go +++ b/internal/entity/voucher.go @@ -9,11 +9,11 @@ type Voucher struct { Creator uint `gorm:"size:255"` Code string `gorm:"size:8"` Desc string - DiscordID string Recipient string - ValidMonths uint + ValidMonths uint8 Amount uint TxHash string + CallerID string gorm.Model } diff --git a/internal/repository/voucher.go b/internal/repository/voucher.go index 7a54f8fe..f25f4bdf 100644 --- a/internal/repository/voucher.go +++ b/internal/repository/voucher.go @@ -25,8 +25,8 @@ func (db *DB) GetVoucherByCode(code string) (entity.Voucher, error) { return voucher, nil } -func (db *DB) UpdateVoucherTx(id uint, txHash string) error { - tx := db.Model(&entity.Voucher{}).Where("id = ?", id).Update("tx_hash", txHash) +func (db *DB) UpdateVoucher(id uint, txHash, callerID string) error { + tx := db.Model(&entity.Voucher{}).Where("id = ?", id).Update("tx_hash", txHash).Update("caller_id", callerID) if tx.Error != nil { return WriteError{ Message: tx.Error.Error(), diff --git a/pkg/utils/random.go b/pkg/utils/random.go new file mode 100644 index 00000000..7110ad36 --- /dev/null +++ b/pkg/utils/random.go @@ -0,0 +1,37 @@ +package utils + +import ( + "math/rand" + "time" + "unsafe" +) + +const ( + Letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + Numbers = "0123456789" + CapitalLetterNumbers = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" +) + +const ( + letterIdxBits = 6 // 6 bits to represent a letter index + letterIdxMask = 1<= 0; { + if remain == 0 { + cache, remain = src.Int63(), letterIdxMax + } + if idx := int(cache & letterIdxMask); idx < len(input) { + b[i] = input[idx] + i-- + } + cache >>= letterIdxBits + remain-- + } + + return *(*string)(unsafe.Pointer(&b)) +} From 9a959a9af685b3ef4ed1ed2c5bd4adb197341618 Mon Sep 17 00:00:00 2001 From: mj Date: Tue, 2 Jul 2024 13:23:10 +0330 Subject: [PATCH 3/7] fix(voucher): create command params --- internal/engine/command/voucher/voucher.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/internal/engine/command/voucher/voucher.go b/internal/engine/command/voucher/voucher.go index 7ee24438..728ed52d 100644 --- a/internal/engine/command/voucher/voucher.go +++ b/internal/engine/command/voucher/voucher.go @@ -59,15 +59,25 @@ func (v *Voucher) GetCommand() command.Command { Help: "Add a new voucher to database", Args: []command.Args{ { - Name: "valid-days", - Desc: "Indicates how many months the voucher is valid after it is issued", - Optional: false, + Name: "recipient", + Desc: "Indicates the name of the recipient of the voucher", + Optional: true, }, { Name: "amount", Desc: "Amount of PAC to bond", Optional: false, }, + { + Name: "valid-months", + Desc: "Indicates how many months the voucher is valid after it is issued", + Optional: false, + }, + { + Name: "description", + Desc: "Describes the reason for issuing the voucher", + Optional: true, + }, }, SubCommands: nil, AppIDs: entity.AllAppIDs(), From 94daf9d3c8da481a94094a2299450efd15e24b6c Mon Sep 17 00:00:00 2001 From: mj Date: Tue, 2 Jul 2024 13:30:41 +0330 Subject: [PATCH 4/7] feat(voucher): claimedBy field added to voucher entity --- internal/engine/command/voucher/claim.go | 2 +- internal/entity/voucher.go | 2 +- internal/repository/voucher.go | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/engine/command/voucher/claim.go b/internal/engine/command/voucher/claim.go index 336560be..7e35753e 100644 --- a/internal/engine/command/voucher/claim.go +++ b/internal/engine/command/voucher/claim.go @@ -44,7 +44,7 @@ func (v *Voucher) claimHandler(cmd command.Command, _ entity.AppID, callerID str return cmd.ErrorResult(errors.New("can't send bond transaction")) } - if err = v.db.UpdateVoucher(voucher.ID, txHash, callerID); err != nil { + if err = v.db.UpdateVoucher(voucher.ID, txHash, cmd.User.ID); err != nil { return cmd.ErrorResult(err) } diff --git a/internal/entity/voucher.go b/internal/entity/voucher.go index 92b034d4..3100e102 100644 --- a/internal/entity/voucher.go +++ b/internal/entity/voucher.go @@ -13,7 +13,7 @@ type Voucher struct { ValidMonths uint8 Amount uint TxHash string - CallerID string + ClaimedBy uint gorm.Model } diff --git a/internal/repository/voucher.go b/internal/repository/voucher.go index f25f4bdf..f4ce9df2 100644 --- a/internal/repository/voucher.go +++ b/internal/repository/voucher.go @@ -25,8 +25,8 @@ func (db *DB) GetVoucherByCode(code string) (entity.Voucher, error) { return voucher, nil } -func (db *DB) UpdateVoucher(id uint, txHash, callerID string) error { - tx := db.Model(&entity.Voucher{}).Where("id = ?", id).Update("tx_hash", txHash).Update("caller_id", callerID) +func (db *DB) UpdateVoucher(id uint, txHash string, claimer uint) error { + tx := db.Model(&entity.Voucher{}).Where("id = ?", id).Update("tx_hash", txHash).Update("claimed_by", claimer) if tx.Error != nil { return WriteError{ Message: tx.Error.Error(), From 78207e17c972d2a3d858f87b381c2279f0d79d7e Mon Sep 17 00:00:00 2001 From: mj Date: Tue, 2 Jul 2024 13:33:34 +0330 Subject: [PATCH 5/7] chore: change phoenix status Help message --- internal/engine/command/phoenix/phoenix.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/engine/command/phoenix/phoenix.go b/internal/engine/command/phoenix/phoenix.go index be07c097..7e4c0751 100644 --- a/internal/engine/command/phoenix/phoenix.go +++ b/internal/engine/command/phoenix/phoenix.go @@ -41,7 +41,7 @@ func (pt *Phoenix) GetCommand() command.Command { subCmdStatus := command.Command{ Name: StatusCommandName, - Help: "Phoenix test-network statistics", + Help: "Phoenix Testnet statistics", Args: []command.Args{}, SubCommands: nil, AppIDs: entity.AllAppIDs(), From 27bf2e165b98840248d5c449e2b57aa4d61d2671 Mon Sep 17 00:00:00 2001 From: mj Date: Tue, 2 Jul 2024 13:35:51 +0330 Subject: [PATCH 6/7] fix: remove faucet_test.go since have issue related to DB mocking --- .../engine/command/phoenix/faucet_test.go | 33 ------------------- 1 file changed, 33 deletions(-) delete mode 100644 internal/engine/command/phoenix/faucet_test.go diff --git a/internal/engine/command/phoenix/faucet_test.go b/internal/engine/command/phoenix/faucet_test.go deleted file mode 100644 index 0f317f57..00000000 --- a/internal/engine/command/phoenix/faucet_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package phoenix - -import ( - "testing" - - "github.com/pagu-project/Pagu/internal/engine/command" - "github.com/pagu-project/Pagu/internal/entity" - "github.com/pagu-project/Pagu/internal/repository" - "github.com/stretchr/testify/assert" -) - -func setup() (Phoenix, command.Command) { - db, _ := repository.NewDB("root:ns1294password@tcp(127.0.0.1:4417)/pagu?parseTime=true") - p := NewPhoenix(nil, 5, nil, *db) - - return p, command.Command{ - Name: FaucetCommandName, - Help: "", - Args: []command.Args{}, - SubCommands: nil, - Middlewares: nil, - AppIDs: entity.AllAppIDs(), - User: &entity.User{ID: 1}, - } -} - -func TestPhoenixFaucet(t *testing.T) { - t.Run("empty args", func(t *testing.T) { - phoenix, cmd := setup() - result := phoenix.faucetHandler(cmd, entity.AppIdDiscord, "") - assert.Equal(t, "An error occurred: invalid wallet address", result.Message) - }) -} From f11b0d77624c6eb018e3259190a41a1beae81e3a Mon Sep 17 00:00:00 2001 From: mj Date: Tue, 2 Jul 2024 14:02:24 +0330 Subject: [PATCH 7/7] chore: change VoucherUpdate method name to ClaimVoucher in voucher repository --- internal/engine/command/voucher/claim.go | 2 +- internal/repository/voucher.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/engine/command/voucher/claim.go b/internal/engine/command/voucher/claim.go index 7e35753e..c028b844 100644 --- a/internal/engine/command/voucher/claim.go +++ b/internal/engine/command/voucher/claim.go @@ -44,7 +44,7 @@ func (v *Voucher) claimHandler(cmd command.Command, _ entity.AppID, callerID str return cmd.ErrorResult(errors.New("can't send bond transaction")) } - if err = v.db.UpdateVoucher(voucher.ID, txHash, cmd.User.ID); err != nil { + if err = v.db.ClaimVoucher(voucher.ID, txHash, cmd.User.ID); err != nil { return cmd.ErrorResult(err) } diff --git a/internal/repository/voucher.go b/internal/repository/voucher.go index f4ce9df2..8007b3d2 100644 --- a/internal/repository/voucher.go +++ b/internal/repository/voucher.go @@ -25,7 +25,7 @@ func (db *DB) GetVoucherByCode(code string) (entity.Voucher, error) { return voucher, nil } -func (db *DB) UpdateVoucher(id uint, txHash string, claimer uint) error { +func (db *DB) ClaimVoucher(id uint, txHash string, claimer uint) error { tx := db.Model(&entity.Voucher{}).Where("id = ?", id).Update("tx_hash", txHash).Update("claimed_by", claimer) if tx.Error != nil { return WriteError{