Skip to content

Commit

Permalink
add initial web3 packet with minimal/test implementation
Browse files Browse the repository at this point in the history
Signed-off-by: p4u <[email protected]>
  • Loading branch information
p4u committed Jan 20, 2025
1 parent 9fbd910 commit ceb6814
Show file tree
Hide file tree
Showing 11 changed files with 1,373 additions and 2 deletions.
16 changes: 16 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ require (
github.com/rs/zerolog v1.33.0
github.com/vocdoni/arbo v0.0.0-20241217102805-a7c0c5f8c359
github.com/vocdoni/circom2gnark v1.0.1-0.20241204100355-b93800bd88a4
github.com/vocdoni/contracts-z v0.0.0-20250113202233-b540833127a6
github.com/vocdoni/gnark-crypto-primitives v0.0.2-0.20241220102053-ba1eee831862
go.vocdoni.io/dvote v1.10.2-0.20241024102542-c1ce6d744bc5
)

require (
github.com/DataDog/zstd v1.5.2 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.14.3 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
Expand All @@ -36,18 +38,27 @@ require (
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
github.com/cometbft/cometbft v1.0.0-alpha.1 // indirect
github.com/consensys/bavard v0.1.24 // indirect
github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c // indirect
github.com/crate-crypto/go-kzg-4844 v1.1.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dchest/blake512 v1.0.0 // indirect
github.com/deckarep/golang-set/v2 v2.6.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
github.com/ethereum/c-kzg-4844 v1.0.0 // indirect
github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/getsentry/sentry-go v0.27.0 // indirect
github.com/glendc/go-external-ip v0.1.0 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/holiman/uint256 v1.3.1 // indirect
github.com/iden3/go-rapidsnark/types v0.0.3 // indirect
github.com/iden3/wasmer-go v0.0.1 // indirect
Expand All @@ -72,13 +83,18 @@ require (
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/ronanh/intcomp v1.1.0 // indirect
github.com/sethvargo/go-retry v0.2.4 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/stretchr/testify v1.9.0 // indirect
github.com/supranational/blst v0.3.13 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
go.mongodb.org/mongo-driver v1.12.1 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
Expand Down
79 changes: 79 additions & 0 deletions go.sum

Large diffs are not rendered by default.

47 changes: 47 additions & 0 deletions types/metadata.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
package types

import (
"encoding/json"
"math/big"
"time"

"github.com/ethereum/go-ethereum/common"
)

type GenericMetadata map[string]string

type MultilingualString map[string]string
Expand Down Expand Up @@ -35,3 +43,42 @@ type Metadata struct {
ProcessType ProcessType `json:"processType"`
BallotMode BallotMode `json:"ballotMode"`
}

type Process struct {
Status uint8 `json:"status"`
OrganizationId common.Address `json:"organizationId"`
EncryptionKey *EncryptionKey `json:"encryptionKey"`
StateRoot HexBytes `json:"stateRoot"`
Result []*big.Int `json:"result"`
StartTime time.Time `json:"startTime"`
Duration time.Duration `json:"duration"`
MetadataURI string `json:"metadataURI"`
BallotMode *BallotMode `json:"ballotMode"`
Census *Census `json:"census"`
}

func (p *Process) String() string {
data, err := json.Marshal(p)
if err != nil {
return ""
}
return string(data)
}

type EncryptionKey struct {
X *big.Int `json:"x"`
Y *big.Int `json:"y"`
}

type Census struct {
CensusOrigin uint8 `json:"censusOrigin"`
MaxVotes *big.Int `json:"maxVotes"`
CensusRoot HexBytes `json:"censusRoot"`
CensusURI string `json:"censusURI"`
}

type OrganizationInfo struct {
Name string
MetadataURI string
ProcessCount uint32
}
4 changes: 2 additions & 2 deletions types/processId.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package types
import (
"bytes"
"encoding/binary"
"encoding/hex"
"fmt"

"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -56,6 +57,5 @@ func (p *ProcessID) UnmarshalBinary(data []byte) error {

// String returns a human readable representation of process ID
func (p *ProcessID) String() string {
return fmt.Sprintf("[chain:%d/addr:%s/nonce:%d]",
p.ChainID, p.Address.Hex(), p.Nonce)
return hex.EncodeToString(p.Marshal())
}
126 changes: 126 additions & 0 deletions web3/cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package main

import (
"context"
"flag"
"math/big"
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/vocdoni/vocdoni-z-sandbox/crypto/ecc/curves"
"github.com/vocdoni/vocdoni-z-sandbox/crypto/elgamal"
"github.com/vocdoni/vocdoni-z-sandbox/log"
"github.com/vocdoni/vocdoni-z-sandbox/types"
"github.com/vocdoni/vocdoni-z-sandbox/util"
"github.com/vocdoni/vocdoni-z-sandbox/web3"
)

var rpcs = []string{
"wss://sepolia.drpc.org",
"https://sepolia.gateway.tenderly.co",
"https://rpc.ankr.com/eth_sepolia",
"https://eth-sepolia.public.blastapi.io",
"https://1rpc.io/sepolia",
}

func main() {
privKey := flag.String("privkey", "", "private key to use for the Ethereum account")
flag.Parse()
log.Init("debug", "stdout", nil)
contracts, err := web3.NewContracts(&web3.Addresses{
OrganizationRegistry: common.HexToAddress("0x3d0b39c0239329955b9F0E8791dF9Aa84133c861"),
ProcessRegistry: common.HexToAddress("0xd512481d0Fa6d975f9B186a9f6e59ea8E12D2C2b"),
}, rpcs[0])
if err != nil {
log.Fatal(err)
}
log.Infow("contracts initialized", "chainId", contracts.ChainID)

for i := 1; i < len(rpcs); i++ {
if err := contracts.AddWeb3Endpoint(rpcs[i]); err != nil {
log.Warnw("failed to add endpoint", "rpc", rpcs[i], "err", err)
}
}

if err := contracts.SetAccountPrivateKey(*privKey); err != nil {
log.Fatal(err)
}

ctx := context.Background()
newProcChan, err := contracts.MonitorProcessCreationByPolling(ctx, time.Second*5)
if err != nil {
log.Fatal(err)
}
go func() {
log.Info("monitoring new processes")
for {
select {
case <-ctx.Done():
return
case proc := <-newProcChan:
log.Infow("new process created", "process", proc.String())
}
}
}()

time.Sleep(20 * time.Second)

orgInfo, err := contracts.Organization(contracts.AccountAddress())
if err != nil {
log.Fatal(err)
}
if orgInfo.MetadataURI == "" {
log.Infof("organization not found, creating it")
txHash, err := contracts.CreateOrganization(&types.OrganizationInfo{
Name: "Vocdoni",
MetadataURI: "https://vocdoni.io",
})
if err != nil {
log.Fatal(err)
}
log.Infow("organization created", "txHash", txHash.Hex())
} else {
log.Infow("organization info", "orgInfo", orgInfo)
}

curve := curves.New(curves.CurveTypeBN254)
pubKey, _, err := elgamal.GenerateKey(curve)
if err != nil {
log.Fatal(err)
}
x, y := pubKey.Point()

pid, txHash, err := contracts.CreateProcess(&types.Process{
Status: 0,
OrganizationId: contracts.AccountAddress(),
EncryptionKey: &types.EncryptionKey{
X: x,
Y: y,
},
StateRoot: util.RandomBytes(32),
StartTime: time.Now().Add(5 * time.Minute),
Duration: time.Hour,
MetadataURI: "https://example.com/metadata",
BallotMode: &types.BallotMode{
MaxCount: 2,
MaxValue: *new(types.BigInt).SetUint64(100),
MinValue: *new(types.BigInt).SetUint64(0),
MaxTotalCost: *new(types.BigInt).SetUint64(0),
MinTotalCost: *new(types.BigInt).SetUint64(0),
ForceUniqueness: false,
CostFromWeight: false,
},
Census: &types.Census{
CensusRoot: util.RandomBytes(32),
MaxVotes: new(big.Int).SetUint64(100),
CensusURI: "https://example.com/census",
CensusOrigin: 0,
},
})
if err != nil {
log.Fatal(err)
}
log.Infow("process created", "pid", pid.String(), "txHash", txHash.Hex())

select {}
}
120 changes: 120 additions & 0 deletions web3/contracts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package web3

import (
"context"
"crypto/ecdsa"
"fmt"
"math/big"
"time"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
bindings "github.com/vocdoni/contracts-z/golang-types"
"github.com/vocdoni/vocdoni-z-sandbox/log"
"github.com/vocdoni/vocdoni-z-sandbox/web3/rpc"
)

// Addresses contains the addresses of the contracts deployed in the network.
type Addresses struct {
OrganizationRegistry common.Address
ProcessRegistry common.Address
ResultsRegistry common.Address
}

// Contracts contains the bindings to the deployed contracts.
type Contracts struct {
ChainID uint64
organizations *bindings.OrganizationRegistry
processes *bindings.ProcessRegistry
web3pool *rpc.Web3Pool
cli *rpc.Client
privKey *ecdsa.PrivateKey
address common.Address

knownProcesses map[string]struct{}
lastWatchBlock uint64
}

// NewContracts creates a new Contracts instance with the given web3 endpoint.
func NewContracts(addresses *Addresses, web3rpc string) (*Contracts, error) {
w3pool := rpc.NewWeb3Pool()
chainID, err := w3pool.AddEndpoint(web3rpc)
if err != nil {
return nil, fmt.Errorf("failed to add web3 endpoint: %w", err)
}
cli, err := w3pool.Client(chainID)
if err != nil {
return nil, fmt.Errorf("failed to get client: %w", err)
}
organizations, err := bindings.NewOrganizationRegistry(addresses.OrganizationRegistry, cli)
if err != nil {
return nil, fmt.Errorf("failed to bind organization registry: %w", err)
}
process, err := bindings.NewProcessRegistry(addresses.ProcessRegistry, cli)
if err != nil {
return nil, fmt.Errorf("failed to bind process registry: %w", err)
}
return &Contracts{
organizations: organizations,
processes: process,
ChainID: chainID,
web3pool: w3pool,
cli: cli,
knownProcesses: make(map[string]struct{}),
}, nil
}

// AddWeb3Endpoint adds a new web3 endpoint to the pool.
func (c *Contracts) AddWeb3Endpoint(web3rpc string) error {
_, err := c.web3pool.AddEndpoint(web3rpc)
return err
}

// SetAccountPrivateKey sets the private key to be used for signing transactions.
func (c *Contracts) SetAccountPrivateKey(hexPrivKey string) error {
var err error
c.privKey, err = crypto.HexToECDSA(hexPrivKey)
if err != nil {
return fmt.Errorf("failed to parse private key: %w", err)
}
c.address = crypto.PubkeyToAddress(c.privKey.PublicKey)
return nil
}

// AccountAddress returns the address of the account used to sign transactions.
func (c *Contracts) AccountAddress() common.Address {
return c.address
}

// authTransactOpts helper method creates the transact options with the private
// key configured in the CommunityHub. It sets the nonce, gas price, and gas
// limit. If something goes wrong creating the signer, getting the nonce, or
// getting the gas price, it returns an error.
func (c *Contracts) authTransactOpts() (*bind.TransactOpts, error) {
if c.privKey == nil {
return nil, fmt.Errorf("no private key set")
}
bChainID := new(big.Int).SetUint64(c.ChainID)
auth, err := bind.NewKeyedTransactorWithChainID(c.privKey, bChainID)
if err != nil {
return nil, fmt.Errorf("failed to create transactor: %w", err)
}
// create the context with a timeout
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// set the nonce
log.Debugw("getting nonce", "address", c.address.Hex())
nonce, err := c.cli.PendingNonceAt(ctx, c.address)
if err != nil {
return nil, fmt.Errorf("failed to get nonce: %w", err)
}
auth.Nonce = new(big.Int).SetUint64(nonce)
// set the gas tip cap
if auth.GasTipCap, err = c.cli.SuggestGasTipCap(ctx); err != nil {
return nil, fmt.Errorf("failed to get gas tip cap: %w", err)
}
// set the gas limit
auth.GasLimit = 10000000
return auth, nil
}
Loading

0 comments on commit ceb6814

Please sign in to comment.