Skip to content

Commit

Permalink
include tx costs from the vochain and increase it for specific txs su…
Browse files Browse the repository at this point in the history
…ch as create or some set election types
  • Loading branch information
lucasmenendez committed Sep 24, 2024
1 parent 410038d commit e66bf4c
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 18 deletions.
6 changes: 6 additions & 0 deletions account/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type Account struct {
client *apiclient.HTTPclient
signer *ethereum.SignKeys

TxCosts map[models.TxType]uint64
ElectionPriceCalc *electionprice.Calculator
}

Expand Down Expand Up @@ -58,9 +59,14 @@ func New(privateKey string, apiEndpoint string) (*Account, error) {
if err != nil {
return nil, fmt.Errorf("failed to initialize election price calculator: %w", err)
}
txCosts, err := vochainTxCosts(apiEndpoint)
if err != nil {
return nil, fmt.Errorf("failed to get transaction costs: %w", err)
}
return &Account{
client: apiClient,
signer: &signer,
TxCosts: txCosts,
ElectionPriceCalc: electionPriceCalc,
}, nil
}
Expand Down
48 changes: 45 additions & 3 deletions account/price.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,20 @@ import (
"net/http"
"time"

"go.vocdoni.io/dvote/api"
"go.vocdoni.io/dvote/vochain/genesis"
"go.vocdoni.io/dvote/vochain/state/electionprice"
"go.vocdoni.io/proto/build/go/models"
)

// electionPriceEndpoint is the endpoint to get the election price factors from
// the Vochain.
const electionPriceEndpoint = "/chain/info/electionPriceFactors"
const (
// electionPriceEndpoint is the endpoint to get the election price factors
// from the Vochain.
electionPriceEndpoint = "/chain/info/electionPriceFactors"
// txCostsEndpoint is the endpoint to get the transaction costs from the
// Vochain.
txCostsEndpoint = "/chain/transactions/cost"
)

// InitElectionPriceCalculator initializes the election price calculator with
// the factors from the Vochain. It returns the election price calculator or an
Expand Down Expand Up @@ -58,3 +66,37 @@ func electionPriceFactors(vochainURI string) (uint64, uint64, electionprice.Fact
}
return data.BasePrice, data.Capacity, data.Factors, nil
}

// vochainTxCosts returns the transaction costs from the Vochain. It returns the
// transaction costs or an error if it fails to get them.
func vochainTxCosts(vochainURI string) (map[models.TxType]uint64, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
// create the request to get the transactions costs
url := vochainURI + txCostsEndpoint
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
// send the request
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to send request: %w", err)
}
defer func() {
_ = resp.Body.Close()
}()
// parse the response
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}
var strTxCosts api.Transaction
if err := json.NewDecoder(resp.Body).Decode(&strTxCosts); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
txCosts := make(map[models.TxType]uint64)
for strType, cost := range strTxCosts.Costs {
txCosts[genesis.TxCostNameToTxType(strType)] = cost
}
return txCosts, nil
}
88 changes: 73 additions & 15 deletions api/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,19 @@ func (a *API) signTxHandler(w http.ResponseWriter, r *http.Request) {
case models.TxType_CREATE_ACCOUNT:
// generate a new faucet package if it's not present and include it in the tx
if txSetAccount.FaucetPackage == nil {
faucetPkg, err := a.account.FaucetPackage(organizationSigner.AddressString(), bootStrapFaucetAmount)
// get the tx cost for the tx type
amount, ok := a.account.TxCosts[models.TxType_CREATE_ACCOUNT]
if !ok {
ErrInvalidTxFormat.With("invalid tx type").Write(w)
return
}
// generate the faucet package with the calculated amount
faucetPkg, err := a.account.FaucetPackage(organizationSigner.AddressString(), amount)
if err != nil {
ErrCouldNotCreateFaucetPackage.WithErr(err).Write(w)
return
}
// include the faucet package in the tx
txSetAccount.FaucetPackage = faucetPkg
tx = &models.Tx{
Payload: &models.Tx_SetAccount{
Expand All @@ -114,20 +122,29 @@ func (a *API) signTxHandler(w http.ResponseWriter, r *http.Request) {
case models.TxType_NEW_PROCESS:
// generate a new faucet package if it's not present and include it in the tx
if txNewProcess.FaucetPackage == nil {
// calculate the election price to fund the faucet package
// with the required amount for the election
amount := a.account.ElectionPriceCalc.Price(&electionprice.ElectionParameters{
// get the tx cost for the tx type
amount, ok := a.account.TxCosts[models.TxType_NEW_PROCESS]
if !ok {
ErrInvalidTxFormat.With("invalid tx type").Write(w)
return
}
// increment the amount with the election price to fund the
// faucet package with the required amount for this type of
// election
amount += a.account.ElectionPriceCalc.Price(&electionprice.ElectionParameters{
MaxCensusSize: txNewProcess.Process.MaxCensusSize,
ElectionDurationSeconds: txNewProcess.Process.Duration,
EncryptedVotes: txNewProcess.Process.EnvelopeType.EncryptedVotes,
AnonymousVotes: txNewProcess.Process.EnvelopeType.Anonymous,
MaxVoteOverwrite: txNewProcess.Process.VoteOptions.MaxVoteOverwrites,
})
// generate the faucet package with the calculated amount
faucetPkg, err := a.account.FaucetPackage(organizationSigner.AddressString(), amount)
if err != nil {
ErrCouldNotCreateFaucetPackage.WithErr(err).Write(w)
return
}
// include the faucet package in the tx
txNewProcess.FaucetPackage = faucetPkg
tx = &models.Tx{
Payload: &models.Tx_NewProcess{
Expand All @@ -143,6 +160,12 @@ func (a *API) signTxHandler(w http.ResponseWriter, r *http.Request) {
ErrInvalidTxFormat.With("missing fields").Write(w)
return
}
// get the tx cost for the tx type
amount, ok := a.account.TxCosts[txSetProcess.Txtype]
if !ok {
ErrInvalidTxFormat.With("invalid tx type").Write(w)
return
}
// check the tx subtype
switch txSetProcess.Txtype {
case models.TxType_SET_PROCESS_STATUS:
Expand All @@ -157,6 +180,23 @@ func (a *API) signTxHandler(w http.ResponseWriter, r *http.Request) {
ErrInvalidTxFormat.With("missing census fields").Write(w)
return
}
// get the current process to fill the missing fields in the tx to
// calculate the election price
currentProcess, err := a.client.Election(txSetProcess.ProcessId)
if err != nil {
ErrVochainRequestFailed.WithErr(err).Write(w)
return
}
// increment the amount with the election price to fund the
// faucet package with the required amount for this type of
// election update
amount += a.account.ElectionPriceCalc.Price(&electionprice.ElectionParameters{
MaxCensusSize: txSetProcess.GetCensusSize(),
ElectionDurationSeconds: uint32(currentProcess.EndDate.Sub(currentProcess.StartDate).Seconds()),
EncryptedVotes: currentProcess.VoteMode.EncryptedVotes,
AnonymousVotes: currentProcess.VoteMode.Anonymous,
MaxVoteOverwrite: currentProcess.TallyMode.MaxVoteOverwrites,
})
case models.TxType_SET_PROCESS_QUESTION_INDEX:
// check if the process question index is in the tx
if txSetProcess.QuestionIndex == nil {
Expand All @@ -175,31 +215,33 @@ func (a *API) signTxHandler(w http.ResponseWriter, r *http.Request) {
ErrInvalidTxFormat.With("missing duration field").Write(w)
return
}
}
// include the faucet package in the tx if it's not present
if txSetProcess.FaucetPackage == nil {
// get the current process to fill the missing fields in the tx
// to calculate the election price
// get the current process to fill the missing fields in the tx to
// calculate the election price
currentProcess, err := a.client.Election(txSetProcess.ProcessId)
if err != nil {
ErrVochainRequestFailed.WithErr(err).Write(w)
return
}
// calculate the election price to fund the faucet package with
// the required amount for the election
amount := a.account.ElectionPriceCalc.Price(&electionprice.ElectionParameters{
MaxCensusSize: txSetProcess.GetCensusSize(),
// increment the amount with the election price to fund the
// faucet package with the required amount for this type of
// election update
amount += a.account.ElectionPriceCalc.Price(&electionprice.ElectionParameters{
MaxCensusSize: currentProcess.Census.MaxCensusSize,
ElectionDurationSeconds: txSetProcess.GetDuration(),
EncryptedVotes: currentProcess.VoteMode.EncryptedVotes,
AnonymousVotes: currentProcess.VoteMode.Anonymous,
MaxVoteOverwrite: currentProcess.TallyMode.MaxVoteOverwrites,
})
}
// include the faucet package in the tx if it's not present
if txSetProcess.FaucetPackage == nil {
// generate the faucet package with the calculated amount
faucetPkg, err := a.account.FaucetPackage(organizationSigner.AddressString(), amount)
if err != nil {
ErrCouldNotCreateFaucetPackage.WithErr(err).Write(w)
return
}
// include the faucet package in the tx
txSetProcess.FaucetPackage = faucetPkg
tx = &models.Tx{
Payload: &models.Tx_SetProcess{
Expand All @@ -216,11 +258,19 @@ func (a *API) signTxHandler(w http.ResponseWriter, r *http.Request) {
}
// include the faucet package in the tx if it's not present
if txSetSIK.FaucetPackage == nil {
faucetPkg, err := a.account.FaucetPackage(organizationSigner.AddressString(), bootStrapFaucetAmount)
// get the tx cost for the tx type
amount, ok := a.account.TxCosts[models.TxType_SET_ACCOUNT_SIK]
if !ok {
ErrInvalidTxFormat.With("invalid tx type").Write(w)
return
}
// generate the faucet package with the calculated amount
faucetPkg, err := a.account.FaucetPackage(organizationSigner.AddressString(), amount)
if err != nil {
ErrCouldNotCreateFaucetPackage.WithErr(err).Write(w)
return
}
// include the faucet package in the tx
txSetSIK.FaucetPackage = faucetPkg
tx = &models.Tx{
Payload: &models.Tx_SetSIK{
Expand All @@ -232,11 +282,19 @@ func (a *API) signTxHandler(w http.ResponseWriter, r *http.Request) {
txCollectFaucet := tx.GetCollectFaucet()
// include the faucet package in the tx if it's not present
if txCollectFaucet.FaucetPackage == nil {
faucetPkg, err := a.account.FaucetPackage(organizationSigner.AddressString(), bootStrapFaucetAmount)
// get the tx cost for the tx type
amount, ok := a.account.TxCosts[models.TxType_COLLECT_FAUCET]
if !ok {
ErrInvalidTxFormat.With("invalid tx type").Write(w)
return
}
// generate the faucet package with the calculated amount
faucetPkg, err := a.account.FaucetPackage(organizationSigner.AddressString(), amount)
if err != nil {
ErrCouldNotCreateFaucetPackage.WithErr(err).Write(w)
return
}
// include the faucet package in the tx
txCollectFaucet.FaucetPackage = faucetPkg
tx = &models.Tx{
Payload: &models.Tx_CollectFaucet{
Expand Down

0 comments on commit e66bf4c

Please sign in to comment.