package blockchain import ( "context" "math/big" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/log" "gitlab.com/pactual1/backend/config" blockchain "gitlab.com/pactual1/backend/services/blockchain/lib" ) type service struct { networkEndpoint string contractAddress string walletAddress string walletPrivateKey string chainID big.Int } func NewService(blockchainConfig config.Blockchain) Service { return &service{networkEndpoint: blockchainConfig.NetworkEndpoint, contractAddress: blockchainConfig.ContractAddress, walletAddress: blockchainConfig.WalletAddress, walletPrivateKey: blockchainConfig.WalletPrivateKey} } type Service interface { CreateContract(ctx context.Context, contractID [32]byte) error AddIOTData(ctx context.Context, contractID [32]byte, deviceID [32]byte, timestamp *big.Int, data []byte) error RegisterNewDeviceIDs(ctx context.Context, contractID [32]byte, deviceIDs [][32]byte) error } func (s *service) signerFunc(address common.Address, txn *types.Transaction) (*types.Transaction, error) { signer := types.NewLondonSigner(&s.chainID) privateKey, err := crypto.HexToECDSA(s.walletPrivateKey) if err != nil { return nil, err } return types.SignTx(txn, signer, privateKey) } func (s *service) getTransactOpts() *bind.TransactOpts { return &bind.TransactOpts{ From: common.HexToAddress(s.walletAddress), Signer: s.signerFunc, } } func (s *service) getClient(ctx context.Context) (*ethclient.Client, *blockchain.Blockchain, error) { // connect to polygon network conn, err := ethclient.Dial(s.networkEndpoint) if err != nil { return nil, nil, err } // create contract client contract, err := blockchain.NewBlockchain(common.HexToAddress(s.contractAddress), conn) if err != nil { return nil, nil, err } chainID, err := conn.ChainID(ctx) if err != nil { return nil, nil, err } s.chainID = *chainID return conn, contract, nil } // track transaction to handle failure in go routine func (s *service) trackTransaction(txn *types.Transaction, conn *ethclient.Client) { receipt, err := bind.WaitMined(context.Background(), conn, txn) defer conn.Close() if err != nil { log.Error("blockchain transaction failed with error", "error", err.Error()) } if receipt.Status != types.ReceiptStatusSuccessful { log.Error("blockchain transaction not successful") } } // create contract with blockchain contract ID func (s *service) CreateContract(ctx context.Context, contractID [32]byte) error { conn, contract, err := s.getClient(ctx) if err != nil { return err } txn, err := contract.CreateContract(s.getTransactOpts(), contractID) if err != nil { return err } go s.trackTransaction(txn, conn) return nil } // map devices with blockchain contract func (s *service) RegisterNewDeviceIDs(ctx context.Context, contractID [32]byte, deviceIDs [][32]byte) error { conn, contract, err := s.getClient(ctx) if err != nil { return err } txn, err := contract.RegisterNewDeviceIds(s.getTransactOpts(), contractID, deviceIDs) if err != nil { return err } go s.trackTransaction(txn, conn) return nil } // store device data with blockchain contract func (s *service) AddIOTData(ctx context.Context, contractID [32]byte, deviceID [32]byte, timestamp *big.Int, data []byte) error { conn, contract, err := s.getClient(ctx) if err != nil { return err } txn, err := contract.AddIOTData(s.getTransactOpts(), contractID, deviceID, timestamp, data) if err != nil { return err } go s.trackTransaction(txn, conn) return nil }