diff --git a/apiclient/account.go b/apiclient/account.go index 756ae7881..2beaed289 100644 --- a/apiclient/account.go +++ b/apiclient/account.go @@ -90,9 +90,7 @@ func (c *HTTPclient) Transfer(to common.Address, amount uint64) (types.HexBytes, // TransferWithNonce sends tokens from the account associated with the client to the given address. // Returns the transaction hash. func (c *HTTPclient) TransferWithNonce(to common.Address, amount uint64, nonce uint32) (types.HexBytes, error) { - var err error - stx := models.SignedTx{} - stx.Tx, err = proto.Marshal(&models.Tx{ + tx, err := proto.Marshal(&models.Tx{ Payload: &models.Tx_SendTokens{ SendTokens: &models.SendTokensTx{ Txtype: models.TxType_SET_ACCOUNT_INFO_URI, @@ -106,7 +104,7 @@ func (c *HTTPclient) TransferWithNonce(to common.Address, amount uint64, nonce u if err != nil { return nil, err } - txHash, _, err := c.SignAndSendTx(&stx) + txHash, _, err := c.SignAndSendTx(tx) return txHash, err } @@ -422,8 +420,7 @@ func (c *HTTPclient) RegisterSIKForVote(electionId types.HexBytes, proof *Census return nil, fmt.Errorf("error generating SIK: %w", err) } // compose and encode the transaction - stx := &models.SignedTx{} - stx.Tx, err = proto.Marshal(&models.Tx{ + tx, err := proto.Marshal(&models.Tx{ Payload: &models.Tx_RegisterSIK{ RegisterSIK: &models.RegisterSIKTx{ SIK: sik, @@ -445,7 +442,7 @@ func (c *HTTPclient) RegisterSIKForVote(electionId types.HexBytes, proof *Census return nil, fmt.Errorf("error encoding RegisterSIKTx: %w", err) } // sign it and send it - hash, _, err := c.SignAndSendTx(stx) + hash, _, err := c.SignAndSendTx(tx) if err != nil { return nil, fmt.Errorf("error signing or sending the Tx: %w", err) } diff --git a/apiclient/blockchain.go b/apiclient/blockchain.go index 09addae16..565c3806a 100644 --- a/apiclient/blockchain.go +++ b/apiclient/blockchain.go @@ -10,7 +10,6 @@ import ( "go.vocdoni.io/dvote/vochain/genesis" "go.vocdoni.io/proto/build/go/models" "google.golang.org/protobuf/encoding/protojson" - "google.golang.org/protobuf/proto" ) // ErrTransactionDoesNotExist is returned when the transaction does not exist @@ -162,60 +161,3 @@ func (c *HTTPclient) TransactionCount() (uint64, error) { return txsCount.Count, json.Unmarshal(resp, txsCount) } - -func (c *HTTPclient) TransactionSetCensus(electionID types.HexBytes, census api.ElectionCensus) (types.HexBytes, error) { - if c.account == nil { - return nil, fmt.Errorf("no account configured") - } - - if _, ok := models.CensusOrigin_value[census.CensusOrigin]; !ok { - return nil, fmt.Errorf("invalid census origin %s", census.CensusOrigin) - } - - // get the own account details - acc, err := c.Account("") - if err != nil { - return nil, fmt.Errorf("could not fetch account info: %s", acc.Address) - } - - tx := &models.SetProcessTx{ - Txtype: models.TxType_SET_PROCESS_CENSUS, - Nonce: acc.Nonce, - ProcessId: electionID, - CensusRoot: census.CensusRoot, - CensusURI: &census.CensusURL, - } - - txb, err := proto.Marshal(&models.Tx{ - Payload: &models.Tx_SetProcess{SetProcess: tx}, - }) - if err != nil { - return nil, err - } - signedTxb, err := c.account.SignVocdoniTx(txb, c.chainID) - if err != nil { - return nil, err - } - stx, err := proto.Marshal( - &models.SignedTx{ - Tx: txb, - Signature: signedTxb, - }) - if err != nil { - return nil, err - } - - // send the transaction - resp, code, err := c.Request(HTTPPOST, &api.Transaction{Payload: stx}, "chain", "transactions") - if err != nil { - return nil, err - } - if code != apirest.HTTPstatusOK { - return nil, fmt.Errorf("%s: %d (%s)", errCodeNot200, code, resp) - } - txResp := new(api.Transaction) - if err := json.Unmarshal(resp, txResp); err != nil { - return nil, err - } - return txResp.Hash, nil -} diff --git a/apiclient/election.go b/apiclient/election.go index 1ea4abcae..c46d1b107 100644 --- a/apiclient/election.go +++ b/apiclient/election.go @@ -301,32 +301,109 @@ func (c *HTTPclient) SetElectionStatus(electionID types.HexBytes, status string) if err != nil { return nil, err } - signedTxb, err := c.account.SignVocdoniTx(txb, c.chainID) + hash, _, err := c.SignAndSendTx(txb) + return hash, err +} + +// SetElectionCensusSize sets the new census size of an election. +func (c *HTTPclient) SetElectionCensusSize(electionID types.HexBytes, newSize uint64) (types.HexBytes, error) { + if c.account == nil { + return nil, fmt.Errorf("no account configured") + } + // get the own account details + acc, err := c.Account("") if err != nil { - return nil, err + return nil, fmt.Errorf("could not fetch account info: %s", acc.Address) } - stx, err := proto.Marshal( - &models.SignedTx{ - Tx: txb, - Signature: signedTxb, - }) + + // build the set process transaction + tx := models.SetProcessTx{ + Txtype: models.TxType_SET_PROCESS_CENSUS, + ProcessId: electionID, + CensusSize: &newSize, + Nonce: acc.Nonce, + } + txb, err := proto.Marshal(&models.Tx{ + Payload: &models.Tx_SetProcess{ + SetProcess: &tx, + }, + }) if err != nil { return nil, err } + hash, _, err := c.SignAndSendTx(txb) + return hash, err +} - // send the transaction - resp, code, err := c.Request(HTTPPOST, &api.Transaction{Payload: stx}, "chain", "transactions") +// SetElectionDuration modify the duration of an election (in seconds). +func (c *HTTPclient) SetElectionDuration(electionID types.HexBytes, newDuration uint32) (types.HexBytes, error) { + if c.account == nil { + return nil, fmt.Errorf("no account configured") + } + // get the own account details + acc, err := c.Account("") + if err != nil { + return nil, fmt.Errorf("could not fetch account info: %s", acc.Address) + } + + // build the set process transaction + tx := models.SetProcessTx{ + Txtype: models.TxType_SET_PROCESS_DURATION, + ProcessId: electionID, + Duration: &newDuration, + Nonce: acc.Nonce, + } + txb, err := proto.Marshal(&models.Tx{ + Payload: &models.Tx_SetProcess{ + SetProcess: &tx, + }, + }) if err != nil { return nil, err } - if code != apirest.HTTPstatusOK { - return nil, fmt.Errorf("%s: %d (%s)", errCodeNot200, code, resp) + hash, _, err := c.SignAndSendTx(txb) + return hash, err +} + +// SetElectionCensus updates the census of an election. Root, URI and size can be updated. +func (c *HTTPclient) SetElectionCensus(electionID types.HexBytes, census api.ElectionCensus) (types.HexBytes, error) { + if c.account == nil { + return nil, fmt.Errorf("no account configured") + } + + if _, ok := models.CensusOrigin_value[census.CensusOrigin]; !ok { + return nil, fmt.Errorf("invalid census origin %s", census.CensusOrigin) + } + + // get the own account details + acc, err := c.Account("") + if err != nil { + return nil, fmt.Errorf("could not fetch account info: %s", acc.Address) + } + + tx := &models.SetProcessTx{ + Txtype: models.TxType_SET_PROCESS_CENSUS, + Nonce: acc.Nonce, + ProcessId: electionID, + CensusRoot: census.CensusRoot, + CensusURI: &census.CensusURL, + CensusSize: func() *uint64 { + if census.MaxCensusSize > 0 { + return &census.MaxCensusSize + } + return nil + }(), } - txResp := new(api.Transaction) - if err := json.Unmarshal(resp, txResp); err != nil { + + txb, err := proto.Marshal(&models.Tx{ + Payload: &models.Tx_SetProcess{SetProcess: tx}, + }) + if err != nil { return nil, err } - return txResp.Hash, nil + + hash, _, err := c.SignAndSendTx(txb) + return hash, err } // ElectionVoteCount returns the number of registered votes for a given election. diff --git a/apiclient/helpers.go b/apiclient/helpers.go index f344aae45..75ba995d5 100644 --- a/apiclient/helpers.go +++ b/apiclient/helpers.go @@ -43,17 +43,26 @@ func (c *HTTPclient) DateToHeight(date time.Time) (uint32, error) { return h.Height, nil } -func (c *HTTPclient) SignAndSendTx(stx *models.SignedTx) (types.HexBytes, []byte, error) { - var err error - if stx.Signature, err = c.account.SignVocdoniTx(stx.Tx, c.ChainID()); err != nil { +// SignAndSendTx signs the given transaction and sends it to the blockchain. +// It returns the transaction hash and the blockchain response (if any). +// Takes a protobuf marshaled transaction as input of type models.Tx +func (c *HTTPclient) SignAndSendTx(marshaledTx []byte) (types.HexBytes, []byte, error) { + // Sign the transaction + sitnature, err := c.account.SignVocdoniTx(marshaledTx, c.ChainID()) + if err != nil { return nil, nil, err } - txData, err := proto.Marshal(stx) + // Build the signed transaction + stx, err := proto.Marshal( + &models.SignedTx{ + Tx: marshaledTx, + Signature: sitnature, + }) if err != nil { return nil, nil, err } - - tx := &api.Transaction{Payload: txData} + // Send the signed transaction and fetch the response + tx := &api.Transaction{Payload: stx} resp, code, err := c.Request(HTTPPOST, tx, "chain", "transactions") if err != nil { return nil, nil, err diff --git a/cmd/end2endtest/dynamicensus.go b/cmd/end2endtest/dynamicensus.go index 36d11fdc3..fe42d3d72 100644 --- a/cmd/end2endtest/dynamicensus.go +++ b/cmd/end2endtest/dynamicensus.go @@ -150,7 +150,7 @@ func (t *E2EDynamicensusElection) Run() error { log.Debugf("election details before set a new census: %s %s %x", t.elections[0].election.Census.CensusOrigin, t.elections[0].election.Census.CensusURL, t.elections[0].election.Census.CensusRoot) - hash, err := api.TransactionSetCensus(electionID, vapi.ElectionCensus{ + hash, err := api.SetElectionCensus(electionID, vapi.ElectionCensus{ CensusOrigin: "OFF_CHAIN_TREE_WEIGHTED", CensusRoot: censusRoot2, CensusURL: "http://test/census", @@ -259,7 +259,7 @@ func (t *E2EDynamicensusElection) Run() error { log.Debugf("election details before: %s %s %x", t.elections[1].election.Census.CensusOrigin, t.elections[1].election.Census.CensusURL, t.elections[1].election.Census.CensusRoot) - if _, err := api.TransactionSetCensus(election.ElectionID, vapi.ElectionCensus{ + if _, err := api.SetElectionCensus(election.ElectionID, vapi.ElectionCensus{ CensusOrigin: "OFF_CHAIN_TREE_WEIGHTED", CensusRoot: censusRoot2, CensusURL: "http://test/census",