Commit eb445d9c authored by Esad Akar's avatar Esad Akar Committed by GitHub

feat: non-mineable overlays (#2108)

parent d0da9fbb
......@@ -61,6 +61,7 @@ const (
optionNameSwapInitialDeposit = "swap-initial-deposit"
optionNameSwapEnable = "swap-enable"
optionNameTransactionHash = "transaction"
optionNameBlockHash = "block-hash"
optionNameSwapDeploymentGasPrice = "swap-deployment-gas-price"
optionNameFullNode = "full-node"
optionNamePostageContractAddress = "postage-stamp-address"
......@@ -238,6 +239,7 @@ func (c *command) setAllFlags(cmd *cobra.Command) {
cmd.Flags().String(optionNamePostageContractAddress, "", "postage stamp contract address")
cmd.Flags().String(optionNamePriceOracleAddress, "", "price oracle contract address")
cmd.Flags().String(optionNameTransactionHash, "", "proof-of-identity transaction hash")
cmd.Flags().String(optionNameBlockHash, "", "block hash of the block whose parent is the block that contains the transaction hash")
cmd.Flags().Uint64(optionNameBlockTime, 15, "chain block time")
cmd.Flags().String(optionNameSwapDeploymentGasPrice, "", "gas price in wei to use for deployment and funding")
cmd.Flags().Duration(optionWarmUpTime, time.Minute*10, "time to warmup the node before pull/push protocols can be kicked off.")
......
......@@ -7,7 +7,9 @@ package cmd
import (
"fmt"
"strings"
"time"
"github.com/ethersphere/bee/pkg/crypto"
"github.com/ethersphere/bee/pkg/node"
"github.com/spf13/cobra"
)
......@@ -34,6 +36,7 @@ func (c *command) initDeployCmd() error {
swapInitialDeposit := c.config.GetString(optionNameSwapInitialDeposit)
swapEndpoint := c.config.GetString(optionNameSwapEndpoint)
deployGasPrice := c.config.GetString(optionNameSwapDeploymentGasPrice)
networkID := c.config.GetUint64(optionNameNetworkID)
stateStore, err := node.InitStateStore(logger, dataDir)
if err != nil {
......@@ -48,11 +51,6 @@ func (c *command) initDeployCmd() error {
}
signer := signerConfig.signer
err = node.CheckOverlayWithStore(signerConfig.address, stateStore)
if err != nil {
return err
}
ctx := cmd.Context()
swapBackend, overlayEthAddress, chainID, transactionMonitor, transactionService, err := node.InitChain(
......@@ -94,6 +92,33 @@ func (c *command) initDeployCmd() error {
swapInitialDeposit,
deployGasPrice,
)
if err != nil {
return err
}
optionTrxHash := c.config.GetString(optionNameTransactionHash)
optionBlockHash := c.config.GetString(optionNameBlockHash)
txHash, err := node.GetTxHash(stateStore, logger, optionTrxHash)
if err != nil {
return fmt.Errorf("invalid transaction hash: %w", err)
}
blockTime := time.Duration(c.config.GetUint64(optionNameBlockTime)) * time.Second
blockHash, err := node.GetTxNextBlock(ctx, logger, swapBackend, transactionMonitor, blockTime, txHash, optionBlockHash)
if err != nil {
return err
}
pubKey, err := signer.PublicKey()
if err != nil {
return err
}
swarmAddress, err := crypto.NewOverlayAddress(*pubKey, networkID, blockHash)
err = node.CheckOverlayWithStore(swarmAddress, stateStore)
return err
},
......
......@@ -26,7 +26,7 @@ func (c *command) initInitCmd() (err error) {
if err != nil {
return fmt.Errorf("new logger: %v", err)
}
signerConfig, err := c.configureSigner(cmd, logger)
_, err = c.configureSigner(cmd, logger)
if err != nil {
return err
}
......@@ -39,7 +39,7 @@ func (c *command) initInitCmd() (err error) {
defer stateStore.Close()
return node.CheckOverlayWithStore(signerConfig.address, stateStore)
return nil
},
PreRunE: func(cmd *cobra.Command, args []string) error {
return c.config.BindPFlags(cmd.Flags())
......
......@@ -31,7 +31,6 @@ import (
"github.com/ethersphere/bee/pkg/logging"
"github.com/ethersphere/bee/pkg/node"
"github.com/ethersphere/bee/pkg/resolver/multiresolver"
"github.com/ethersphere/bee/pkg/swarm"
"github.com/kardianos/service"
"github.com/spf13/cobra"
)
......@@ -116,7 +115,7 @@ Welcome to the Swarm.... Bzzz Bzzzz Bzzzz
return errors.New("boot node must be started as a full node")
}
b, err := node.NewBee(c.config.GetString(optionNameP2PAddr), signerConfig.address, *signerConfig.publicKey, signerConfig.signer, c.config.GetUint64(optionNameNetworkID), logger, signerConfig.libp2pPrivateKey, signerConfig.pssPrivateKey, node.Options{
b, err := node.NewBee(c.config.GetString(optionNameP2PAddr), signerConfig.publicKey, signerConfig.signer, c.config.GetUint64(optionNameNetworkID), logger, signerConfig.libp2pPrivateKey, signerConfig.pssPrivateKey, &node.Options{
DataDir: c.config.GetString(optionNameDataDir),
CacheCapacity: c.config.GetUint64(optionNameCacheCapacity),
DBOpenFilesLimit: c.config.GetUint64(optionNameDBOpenFilesLimit),
......@@ -151,6 +150,7 @@ Welcome to the Swarm.... Bzzz Bzzzz Bzzzz
SwapEnable: c.config.GetBool(optionNameSwapEnable),
FullNodeMode: fullNode,
Transaction: c.config.GetString(optionNameTransactionHash),
BlockHash: c.config.GetString(optionNameBlockHash),
PostageContractAddress: c.config.GetString(optionNamePostageContractAddress),
PriceOracleAddress: c.config.GetString(optionNamePriceOracleAddress),
BlockTime: c.config.GetUint64(optionNameBlockTime),
......@@ -247,7 +247,6 @@ func (p *program) Stop(s service.Service) error {
type signerConfig struct {
signer crypto.Signer
address swarm.Address
publicKey *ecdsa.PublicKey
libp2pPrivateKey *ecdsa.PrivateKey
pssPrivateKey *ecdsa.PrivateKey
......@@ -279,7 +278,6 @@ func (c *command) configureSigner(cmd *cobra.Command, logger logging.Logger) (co
}
var signer crypto.Signer
var address swarm.Address
var password string
var publicKey *ecdsa.PublicKey
if p := c.config.GetString(optionNamePassword); p != "" {
......@@ -347,32 +345,14 @@ func (c *command) configureSigner(cmd *cobra.Command, logger logging.Logger) (co
if err != nil {
return nil, err
}
address, err = crypto.NewOverlayAddress(*publicKey, c.config.GetUint64(optionNameNetworkID))
if err != nil {
return nil, err
}
logger.Infof("using swarm network address through clef: %s", address)
} else {
logger.Warning("clef is not enabled; portability and security of your keys is sub optimal")
swarmPrivateKey, created, err := keystore.Key("swarm", password)
swarmPrivateKey, _, err := keystore.Key("swarm", password)
if err != nil {
return nil, fmt.Errorf("swarm key: %w", err)
}
signer = crypto.NewDefaultSigner(swarmPrivateKey)
publicKey = &swarmPrivateKey.PublicKey
address, err = crypto.NewOverlayAddress(*publicKey, c.config.GetUint64(optionNameNetworkID))
if err != nil {
return nil, err
}
if created {
logger.Infof("new swarm network address created: %s", address)
} else {
logger.Infof("using existing swarm network address: %s", address)
}
}
logger.Infof("swarm public key %x", crypto.EncodeSecp256k1PublicKey(publicKey))
......@@ -408,7 +388,6 @@ func (c *command) configureSigner(cmd *cobra.Command, logger logging.Logger) (co
return &signerConfig{
signer: signer,
address: address,
publicKey: publicKey,
libp2pPrivateKey: libp2pPrivateKey,
pssPrivateKey: pssPrivateKey,
......
......@@ -22,7 +22,6 @@ require (
github.com/gorilla/mux v1.7.4
github.com/gorilla/websocket v1.4.2
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/golang-lru v0.5.4
github.com/kardianos/service v1.2.0
github.com/koron/go-ssdp v0.0.2 // indirect
github.com/kr/text v0.2.0 // indirect
......@@ -65,14 +64,12 @@ require (
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.16.0 // indirect
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
golang.org/x/mod v0.3.0 // indirect
golang.org/x/net v0.0.0-20201224014010-6772e930b67b
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf
golang.org/x/text v0.3.4 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0
golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/ini.v1 v1.57.0 // indirect
gopkg.in/yaml.v2 v2.3.0
......
......@@ -967,7 +967,6 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/xtaci/kcp-go v5.4.20+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
gitlab.com/nolash/go-mockbytes v0.0.7 h1:9XVFpEfY67kGBVJve3uV19kzqORdlo7V+q09OE6Yo54=
gitlab.com/nolash/go-mockbytes v0.0.7/go.mod h1:KKOpNTT39j2Eo+P6uUTOncntfeKY6AFh/2CxuD5MpgE=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
......@@ -1053,10 +1052,8 @@ golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd h1:ePuNC7PZ6O5BzgPn9bZayERXBdfZjUYoXEf5BTfDfh8=
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
......@@ -1084,7 +1081,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
......@@ -1203,9 +1199,8 @@ golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200221224223-e1da425f72fd h1:hHkvGJK23seRCflePJnVa9IMv8fsuavSCWKd11kDQFs=
golang.org/x/tools v0.0.0-20200221224223-e1da425f72fd/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f h1:JcoF/bowzCDI+MXu1yLqQGNO3ibqWsWq+Sk7pOT218w=
golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
......
......@@ -7,6 +7,7 @@ package addressbook_test
import (
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethersphere/bee/pkg/addressbook"
"github.com/ethersphere/bee/pkg/bzz"
"github.com/ethersphere/bee/pkg/crypto"
......@@ -30,6 +31,7 @@ func run(t *testing.T, f bookFunc) {
store := f(t)
addr1 := swarm.NewAddress([]byte{0, 1, 2, 3})
addr2 := swarm.NewAddress([]byte{0, 1, 2, 4})
trxHash := common.HexToHash("0x1").Bytes()
multiaddr, err := ma.NewMultiaddr("/ip4/1.1.1.1")
if err != nil {
t.Fatal(err)
......@@ -40,7 +42,7 @@ func run(t *testing.T, f bookFunc) {
t.Fatal(err)
}
bzzAddr, err := bzz.NewAddress(crypto.NewDefaultSigner(pk), multiaddr, addr1, 1)
bzzAddr, err := bzz.NewAddress(crypto.NewDefaultSigner(pk), multiaddr, addr1, 1, trxHash)
if err != nil {
t.Fatal(err)
}
......
......@@ -15,6 +15,7 @@ import (
"errors"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethersphere/bee/pkg/crypto"
"github.com/ethersphere/bee/pkg/swarm"
......@@ -27,18 +28,20 @@ var ErrInvalidAddress = errors.New("invalid address")
// It consists of a peers underlay (physical) address, overlay (topology) address and signature.
// Signature is used to verify the `Overlay/Underlay` pair, as it is based on `underlay|networkID`, signed with the public key of Overlay address
type Address struct {
Underlay ma.Multiaddr
Overlay swarm.Address
Signature []byte
Underlay ma.Multiaddr
Overlay swarm.Address
Signature []byte
Transaction []byte
}
type addressJSON struct {
Overlay string `json:"overlay"`
Underlay string `json:"underlay"`
Signature string `json:"signature"`
Overlay string `json:"overlay"`
Underlay string `json:"underlay"`
Signature string `json:"signature"`
Transaction string `json:"transaction"`
}
func NewAddress(signer crypto.Signer, underlay ma.Multiaddr, overlay swarm.Address, networkID uint64) (*Address, error) {
func NewAddress(signer crypto.Signer, underlay ma.Multiaddr, overlay swarm.Address, networkID uint64, trx []byte) (*Address, error) {
underlayBinary, err := underlay.MarshalBinary()
if err != nil {
return nil, err
......@@ -50,19 +53,20 @@ func NewAddress(signer crypto.Signer, underlay ma.Multiaddr, overlay swarm.Addre
}
return &Address{
Underlay: underlay,
Overlay: overlay,
Signature: signature,
Underlay: underlay,
Overlay: overlay,
Signature: signature,
Transaction: trx,
}, nil
}
func ParseAddress(underlay, overlay, signature []byte, networkID uint64) (*Address, error) {
func ParseAddress(underlay, overlay, signature, trxHash, blockHash []byte, networkID uint64) (*Address, error) {
recoveredPK, err := crypto.Recover(signature, generateSignData(underlay, overlay, networkID))
if err != nil {
return nil, ErrInvalidAddress
}
recoveredOverlay, err := crypto.NewOverlayAddress(*recoveredPK, networkID)
recoveredOverlay, err := crypto.NewOverlayAddress(*recoveredPK, networkID, blockHash)
if err != nil {
return nil, ErrInvalidAddress
}
......@@ -76,9 +80,10 @@ func ParseAddress(underlay, overlay, signature []byte, networkID uint64) (*Addre
}
return &Address{
Underlay: multiUnderlay,
Overlay: swarm.NewAddress(overlay),
Signature: signature,
Underlay: multiUnderlay,
Overlay: swarm.NewAddress(overlay),
Signature: signature,
Transaction: trxHash,
}, nil
}
......@@ -91,14 +96,15 @@ func generateSignData(underlay, overlay []byte, networkID uint64) []byte {
}
func (a *Address) Equal(b *Address) bool {
return a.Overlay.Equal(b.Overlay) && a.Underlay.Equal(b.Underlay) && bytes.Equal(a.Signature, b.Signature)
return a.Overlay.Equal(b.Overlay) && a.Underlay.Equal(b.Underlay) && bytes.Equal(a.Signature, b.Signature) && bytes.Equal(a.Transaction, b.Transaction)
}
func (a *Address) MarshalJSON() ([]byte, error) {
return json.Marshal(&addressJSON{
Overlay: a.Overlay.String(),
Underlay: a.Underlay.String(),
Signature: base64.StdEncoding.EncodeToString(a.Signature),
Overlay: a.Overlay.String(),
Underlay: a.Underlay.String(),
Signature: base64.StdEncoding.EncodeToString(a.Signature),
Transaction: common.Bytes2Hex(a.Transaction),
})
}
......@@ -123,11 +129,12 @@ func (a *Address) UnmarshalJSON(b []byte) error {
a.Underlay = m
a.Signature, err = base64.StdEncoding.DecodeString(v.Signature)
a.Transaction = common.Hex2Bytes(v.Transaction)
return err
}
func (a *Address) String() string {
return fmt.Sprintf("[Underlay: %v, Overlay %v, Signature %x]", a.Underlay, a.Overlay, a.Signature)
return fmt.Sprintf("[Underlay: %v, Overlay %v, Signature %x, Transaction %x]", a.Underlay, a.Overlay, a.Signature, a.Transaction)
}
// ShortString returns shortened versions of bzz address in a format: [Overlay, Underlay]
......
......@@ -7,6 +7,7 @@ package bzz_test
import (
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethersphere/bee/pkg/bzz"
"github.com/ethersphere/bee/pkg/crypto"
......@@ -19,23 +20,26 @@ func TestBzzAddress(t *testing.T) {
t.Fatal(err)
}
trxHash := common.HexToHash("0x1").Bytes()
blockHash := common.HexToHash("0x2").Bytes()
privateKey1, err := crypto.GenerateSecp256k1Key()
if err != nil {
t.Fatal(err)
}
overlay, err := crypto.NewOverlayAddress(privateKey1.PublicKey, 3)
overlay, err := crypto.NewOverlayAddress(privateKey1.PublicKey, 3, blockHash)
if err != nil {
t.Fatal(err)
}
signer1 := crypto.NewDefaultSigner(privateKey1)
bzzAddress, err := bzz.NewAddress(signer1, node1ma, overlay, 3)
bzzAddress, err := bzz.NewAddress(signer1, node1ma, overlay, 3, trxHash)
if err != nil {
t.Fatal(err)
}
bzzAddress2, err := bzz.ParseAddress(node1ma.Bytes(), overlay.Bytes(), bzzAddress.Signature, 3)
bzzAddress2, err := bzz.ParseAddress(node1ma.Bytes(), overlay.Bytes(), bzzAddress.Signature, trxHash, blockHash, 3)
if err != nil {
t.Fatal(err)
}
......
......@@ -20,24 +20,34 @@ import (
// RecoverFunc is a function to recover the public key from a signature
type RecoverFunc func(signature, data []byte) (*ecdsa.PublicKey, error)
var ErrBadHashLength = errors.New("wrong block hash length")
const (
AddressSize = 20
)
// NewOverlayAddress constructs a Swarm Address from ECDSA public key.
func NewOverlayAddress(p ecdsa.PublicKey, networkID uint64) (swarm.Address, error) {
func NewOverlayAddress(p ecdsa.PublicKey, networkID uint64, blockHash []byte) (swarm.Address, error) {
ethAddr, err := NewEthereumAddress(p)
if err != nil {
return swarm.ZeroAddress, err
}
return NewOverlayFromEthereumAddress(ethAddr, networkID), nil
if len(blockHash) != 32 {
return swarm.ZeroAddress, ErrBadHashLength
}
return NewOverlayFromEthereumAddress(ethAddr, networkID, blockHash), nil
}
// NewOverlayFromEthereumAddress constructs a Swarm Address for an Ethereum address.
func NewOverlayFromEthereumAddress(ethAddr []byte, networkID uint64) swarm.Address {
func NewOverlayFromEthereumAddress(ethAddr []byte, networkID uint64, blockHash []byte) swarm.Address {
netIDBytes := make([]byte, 8)
binary.LittleEndian.PutUint64(netIDBytes, networkID)
h := sha3.Sum256(append(ethAddr, netIDBytes...))
data := append(ethAddr, netIDBytes...)
data = append(data, blockHash...)
h := sha3.Sum256(data)
return swarm.NewAddress(h[:])
}
......
......@@ -7,8 +7,10 @@ package crypto_test
import (
"bytes"
"encoding/hex"
"errors"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethersphere/bee/pkg/crypto"
)
......@@ -38,13 +40,18 @@ func TestNewAddress(t *testing.T) {
if err != nil {
t.Fatal(err)
}
a, err := crypto.NewOverlayAddress(k.PublicKey, 1)
a, err := crypto.NewOverlayAddress(k.PublicKey, 1, common.HexToHash("0x1").Bytes())
if err != nil {
t.Fatal(err)
}
if l := len(a.Bytes()); l != 32 {
t.Errorf("got address length %v, want %v", l, 32)
}
_, err = crypto.NewOverlayAddress(k.PublicKey, 1, nil)
if !errors.Is(err, crypto.ErrBadHashLength) {
t.Fatalf("expected %v, got %v", crypto.ErrBadHashLength, err)
}
}
func TestEncodeSecp256k1PrivateKey(t *testing.T) {
......
......@@ -33,7 +33,7 @@ import (
// Service implements http.Handler interface to be used in HTTP server.
type Service struct {
overlay swarm.Address
overlay *swarm.Address
publicKey ecdsa.PublicKey
pssPublicKey ecdsa.PublicKey
ethereumAddress common.Address
......@@ -63,9 +63,8 @@ type Service struct {
// to expose /addresses, /health endpoints, Go metrics and pprof. It is useful to expose
// these endpoints before all dependencies are configured and injected to have
// access to basic debugging tools and /health endpoint.
func New(overlay swarm.Address, publicKey, pssPublicKey ecdsa.PublicKey, ethereumAddress common.Address, logger logging.Logger, tracer *tracing.Tracer, corsAllowedOrigins []string) *Service {
func New(publicKey, pssPublicKey ecdsa.PublicKey, ethereumAddress common.Address, logger logging.Logger, tracer *tracing.Tracer, corsAllowedOrigins []string) *Service {
s := new(Service)
s.overlay = overlay
s.publicKey = publicKey
s.pssPublicKey = pssPublicKey
s.ethereumAddress = ethereumAddress
......@@ -82,7 +81,7 @@ func New(overlay swarm.Address, publicKey, pssPublicKey ecdsa.PublicKey, ethereu
// Configure injects required dependencies and configuration parameters and
// constructs HTTP routes that depend on them. It is intended and safe to call
// this method only once.
func (s *Service) Configure(p2p p2p.DebugService, pingpong pingpong.Interface, topologyDriver topology.Driver, lightNodes *lightnode.Container, storer storage.Storer, tags *tags.Tags, accounting accounting.Interface, pseudosettle settlement.Interface, chequebookEnabled bool, swap swap.Interface, chequebook chequebook.Service, batchStore postage.Storer, transaction transaction.Service) {
func (s *Service) Configure(overlay swarm.Address, p2p p2p.DebugService, pingpong pingpong.Interface, topologyDriver topology.Driver, lightNodes *lightnode.Container, storer storage.Storer, tags *tags.Tags, accounting accounting.Interface, pseudosettle settlement.Interface, chequebookEnabled bool, swap swap.Interface, chequebook chequebook.Service, batchStore postage.Storer, transaction transaction.Service) {
s.p2p = p2p
s.pingpong = pingpong
s.topologyDriver = topologyDriver
......@@ -96,6 +95,7 @@ func (s *Service) Configure(p2p p2p.DebugService, pingpong pingpong.Interface, t
s.batchStore = batchStore
s.pseudosettle = pseudosettle
s.transaction = transaction
s.overlay = &overlay
s.setRouter(s.newRouter())
}
......
......@@ -70,8 +70,8 @@ func newTestServer(t *testing.T, o testServerOptions) *testServer {
swapserv := swapmock.New(o.SwapOpts...)
transaction := transactionmock.New(o.TransactionOpts...)
ln := lightnode.NewContainer(o.Overlay)
s := debugapi.New(o.Overlay, o.PublicKey, o.PSSPublicKey, o.EthereumAddress, logging.New(ioutil.Discard, 0), nil, o.CORSAllowedOrigins)
s.Configure(o.P2P, o.Pingpong, topologyDriver, ln, o.Storer, o.Tags, acc, settlement, true, swapserv, chequebook, o.BatchStore, transaction)
s := debugapi.New(o.PublicKey, o.PSSPublicKey, o.EthereumAddress, logging.New(ioutil.Discard, 0), nil, o.CORSAllowedOrigins)
s.Configure(o.Overlay, o.P2P, o.Pingpong, topologyDriver, ln, o.Storer, o.Tags, acc, settlement, true, swapserv, chequebook, o.BatchStore, transaction)
ts := httptest.NewServer(s)
t.Cleanup(ts.Close)
......@@ -138,7 +138,7 @@ func TestServer_Configure(t *testing.T) {
swapserv := swapmock.New(o.SwapOpts...)
ln := lightnode.NewContainer(o.Overlay)
transaction := transactionmock.New(o.TransactionOpts...)
s := debugapi.New(o.Overlay, o.PublicKey, o.PSSPublicKey, o.EthereumAddress, logging.New(ioutil.Discard, 0), nil, nil)
s := debugapi.New(o.PublicKey, o.PSSPublicKey, o.EthereumAddress, logging.New(ioutil.Discard, 0), nil, nil)
ts := httptest.NewServer(s)
t.Cleanup(ts.Close)
......@@ -162,7 +162,6 @@ func TestServer_Configure(t *testing.T) {
)
jsonhttptest.Request(t, client, http.MethodGet, "/addresses", http.StatusOK,
jsonhttptest.WithExpectedJSONResponse(debugapi.AddressesResponse{
Overlay: o.Overlay,
Underlay: make([]multiaddr.Multiaddr, 0),
Ethereum: o.EthereumAddress,
PublicKey: hex.EncodeToString(crypto.EncodeSecp256k1PublicKey(&o.PublicKey)),
......@@ -170,7 +169,7 @@ func TestServer_Configure(t *testing.T) {
}),
)
s.Configure(o.P2P, o.Pingpong, topologyDriver, ln, o.Storer, o.Tags, acc, settlement, true, swapserv, chequebook, nil, transaction)
s.Configure(o.Overlay, o.P2P, o.Pingpong, topologyDriver, ln, o.Storer, o.Tags, acc, settlement, true, swapserv, chequebook, nil, transaction)
testBasicRouter(t, client)
jsonhttptest.Request(t, client, http.MethodGet, "/readiness", http.StatusOK,
......@@ -181,7 +180,7 @@ func TestServer_Configure(t *testing.T) {
)
jsonhttptest.Request(t, client, http.MethodGet, "/addresses", http.StatusOK,
jsonhttptest.WithExpectedJSONResponse(debugapi.AddressesResponse{
Overlay: o.Overlay,
Overlay: &o.Overlay,
Underlay: addresses,
Ethereum: o.EthereumAddress,
PublicKey: hex.EncodeToString(crypto.EncodeSecp256k1PublicKey(&o.PublicKey)),
......
......@@ -16,7 +16,7 @@ import (
)
type addressesResponse struct {
Overlay swarm.Address `json:"overlay"`
Overlay *swarm.Address `json:"overlay"`
Underlay []multiaddr.Multiaddr `json:"underlay"`
Ethereum common.Address `json:"ethereum"`
PublicKey string `json:"publicKey"`
......
......@@ -51,7 +51,7 @@ func TestAddresses(t *testing.T) {
t.Run("ok", func(t *testing.T) {
jsonhttptest.Request(t, testServer.Client, http.MethodGet, "/addresses", http.StatusOK,
jsonhttptest.WithExpectedJSONResponse(debugapi.AddressesResponse{
Overlay: overlay,
Overlay: &overlay,
Underlay: addresses,
Ethereum: ethereumAddress,
PublicKey: hex.EncodeToString(crypto.EncodeSecp256k1PublicKey(&privateKey.PublicKey)),
......
......@@ -10,6 +10,7 @@ import (
"net/http"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethersphere/bee/pkg/bzz"
"github.com/ethersphere/bee/pkg/crypto"
"github.com/ethersphere/bee/pkg/debugapi"
......@@ -31,7 +32,9 @@ func TestConnect(t *testing.T) {
t.Fatal(err)
}
overlay, err := crypto.NewOverlayAddress(privateKey.PublicKey, 0)
block := common.HexToHash("0x1").Bytes()
overlay, err := crypto.NewOverlayAddress(privateKey.PublicKey, 0, block)
if err != nil {
t.Fatal(err)
}
......@@ -40,7 +43,7 @@ func TestConnect(t *testing.T) {
t.Fatal(err)
}
bzzAddress, err := bzz.NewAddress(crypto.NewDefaultSigner(privateKey), underlama, overlay, 0)
bzzAddress, err := bzz.NewAddress(crypto.NewDefaultSigner(privateKey), underlama, overlay, 0, nil)
if err != nil {
t.Fatal(err)
}
......
......@@ -24,6 +24,7 @@ import (
"github.com/ethersphere/bee/pkg/p2p"
"github.com/ethersphere/bee/pkg/p2p/protobuf"
"github.com/ethersphere/bee/pkg/swarm"
ma "github.com/multiformats/go-multiaddr"
"golang.org/x/time/rate"
)
......@@ -112,7 +113,9 @@ func (s *Service) sendPeers(ctx context.Context, peer swarm.Address, peers []swa
if err != nil {
_ = stream.Reset()
} else {
_ = stream.FullClose()
// added this because Recorder (unit test) emits an unnecessary EOF when Close is called
time.Sleep(time.Millisecond * 50)
_ = stream.Close()
}
}()
w, _ := protobuf.NewWriterAndReader(stream)
......@@ -128,9 +131,10 @@ func (s *Service) sendPeers(ctx context.Context, peer swarm.Address, peers []swa
}
peersRequest.Peers = append(peersRequest.Peers, &pb.BzzAddress{
Overlay: addr.Overlay.Bytes(),
Underlay: addr.Underlay.Bytes(),
Signature: addr.Signature,
Overlay: addr.Overlay.Bytes(),
Underlay: addr.Underlay.Bytes(),
Signature: addr.Signature,
Transaction: addr.Transaction,
})
}
......@@ -166,13 +170,21 @@ func (s *Service) peersHandler(ctx context.Context, peer p2p.Peer, stream p2p.St
var peers []swarm.Address
for _, newPeer := range peersReq.Peers {
bzzAddress, err := bzz.ParseAddress(newPeer.Underlay, newPeer.Overlay, newPeer.Signature, s.networkID)
multiUnderlay, err := ma.NewMultiaddrBytes(newPeer.Underlay)
if err != nil {
s.logger.Warningf("skipping peer in response %s: %v", newPeer.String(), err)
s.logger.Errorf("hive: multi address underlay err: %v", err)
continue
}
err = s.addressBook.Put(bzzAddress.Overlay, *bzzAddress)
bzzAddress := bzz.Address{
Overlay: swarm.NewAddress(newPeer.Overlay),
Underlay: multiUnderlay,
Signature: newPeer.Signature,
Transaction: newPeer.Transaction,
}
err = s.addressBook.Put(bzzAddress.Overlay, bzzAddress)
if err != nil {
s.logger.Warningf("skipping peer in response %s: %v", newPeer.String(), err)
continue
......@@ -193,7 +205,7 @@ func (s *Service) rateLimitPeer(peer swarm.Address, count int) error {
s.limiterLock.Lock()
defer s.limiterLock.Unlock()
addr := peer.String()
addr := peer.ByteString()
limiter, ok := s.limiter[addr]
if !ok {
......
......@@ -16,6 +16,7 @@ import (
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
ma "github.com/multiformats/go-multiaddr"
ab "github.com/ethersphere/bee/pkg/addressbook"
......@@ -31,6 +32,11 @@ import (
"github.com/ethersphere/bee/pkg/swarm/test"
)
var (
tx = common.HexToHash("0x2").Bytes()
block = common.HexToHash("0x1").Bytes()
)
func TestHandlerRateLimit(t *testing.T) {
logger := logging.New(ioutil.Discard, 0)
......@@ -63,11 +69,11 @@ func TestHandlerRateLimit(t *testing.T) {
t.Fatal(err)
}
signer := crypto.NewDefaultSigner(pk)
overlay, err := crypto.NewOverlayAddress(pk.PublicKey, networkID)
overlay, err := crypto.NewOverlayAddress(pk.PublicKey, networkID, block)
if err != nil {
t.Fatal(err)
}
bzzAddr, err := bzz.NewAddress(signer, underlay, overlay, networkID)
bzzAddr, err := bzz.NewAddress(signer, underlay, overlay, networkID, tx)
if err != nil {
t.Fatal(err)
}
......@@ -86,7 +92,6 @@ func TestHandlerRateLimit(t *testing.T) {
t.Fatal(err)
}
// // get a record for this stream
rec, err := serverRecorder.Records(serverAddress, "hive", "1.0.0", "peers")
if err != nil {
t.Fatal(err)
......@@ -125,11 +130,11 @@ func TestBroadcastPeers(t *testing.T) {
t.Fatal(err)
}
signer := crypto.NewDefaultSigner(pk)
overlay, err := crypto.NewOverlayAddress(pk.PublicKey, networkID)
overlay, err := crypto.NewOverlayAddress(pk.PublicKey, networkID, block)
if err != nil {
t.Fatal(err)
}
bzzAddr, err := bzz.NewAddress(signer, underlay, overlay, networkID)
bzzAddr, err := bzz.NewAddress(signer, underlay, overlay, networkID, tx)
if err != nil {
t.Fatal(err)
}
......@@ -142,9 +147,10 @@ func TestBroadcastPeers(t *testing.T) {
}
wantMsgs[i/hive.MaxBatchSize].Peers = append(wantMsgs[i/hive.MaxBatchSize].Peers, &pb.BzzAddress{
Overlay: bzzAddresses[i].Overlay.Bytes(),
Underlay: bzzAddresses[i].Underlay.Bytes(),
Signature: bzzAddresses[i].Signature,
Overlay: bzzAddresses[i].Overlay.Bytes(),
Underlay: bzzAddresses[i].Underlay.Bytes(),
Signature: bzzAddresses[i].Signature,
Transaction: tx,
})
}
......
......@@ -67,9 +67,10 @@ func (m *Peers) GetPeers() []*BzzAddress {
}
type BzzAddress struct {
Underlay []byte `protobuf:"bytes,1,opt,name=Underlay,proto3" json:"Underlay,omitempty"`
Signature []byte `protobuf:"bytes,2,opt,name=Signature,proto3" json:"Signature,omitempty"`
Overlay []byte `protobuf:"bytes,3,opt,name=Overlay,proto3" json:"Overlay,omitempty"`
Underlay []byte `protobuf:"bytes,1,opt,name=Underlay,proto3" json:"Underlay,omitempty"`
Signature []byte `protobuf:"bytes,2,opt,name=Signature,proto3" json:"Signature,omitempty"`
Overlay []byte `protobuf:"bytes,3,opt,name=Overlay,proto3" json:"Overlay,omitempty"`
Transaction []byte `protobuf:"bytes,4,opt,name=Transaction,proto3" json:"Transaction,omitempty"`
}
func (m *BzzAddress) Reset() { *m = BzzAddress{} }
......@@ -126,6 +127,13 @@ func (m *BzzAddress) GetOverlay() []byte {
return nil
}
func (m *BzzAddress) GetTransaction() []byte {
if m != nil {
return m.Transaction
}
return nil
}
func init() {
proto.RegisterType((*Peers)(nil), "hive.Peers")
proto.RegisterType((*BzzAddress)(nil), "hive.BzzAddress")
......@@ -134,18 +142,20 @@ func init() {
func init() { proto.RegisterFile("hive.proto", fileDescriptor_d635d1ead41ba02c) }
var fileDescriptor_d635d1ead41ba02c = []byte{
// 174 bytes of a gzipped FileDescriptorProto
// 196 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xca, 0xc8, 0x2c, 0x4b,
0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x01, 0xb1, 0x95, 0xf4, 0xb9, 0x58, 0x03, 0x52,
0x53, 0x8b, 0x8a, 0x85, 0xd4, 0xb8, 0x58, 0x0b, 0x40, 0x0c, 0x09, 0x46, 0x05, 0x66, 0x0d, 0x6e,
0x23, 0x01, 0x3d, 0xb0, 0x52, 0xa7, 0xaa, 0x2a, 0xc7, 0x94, 0x94, 0xa2, 0xd4, 0xe2, 0xe2, 0x20,
0x88, 0xb4, 0x52, 0x02, 0x17, 0x17, 0x42, 0x50, 0x48, 0x8a, 0x8b, 0x23, 0x34, 0x2f, 0x25, 0xb5,
0x28, 0x27, 0xb1, 0x52, 0x82, 0x51, 0x81, 0x51, 0x83, 0x27, 0x08, 0xce, 0x17, 0x92, 0xe1, 0xe2,
0x0c, 0xce, 0x4c, 0xcf, 0x4b, 0x2c, 0x29, 0x2d, 0x4a, 0x95, 0x60, 0x02, 0x4b, 0x22, 0x04, 0x84,
0x24, 0xb8, 0xd8, 0xfd, 0xcb, 0x20, 0x1a, 0x99, 0xc1, 0x72, 0x30, 0xae, 0x93, 0xcc, 0x89, 0x47,
0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0xc3, 0x85,
0xc7, 0x72, 0x0c, 0x37, 0x1e, 0xcb, 0x31, 0x44, 0x31, 0x15, 0x24, 0x25, 0xb1, 0x81, 0x5d, 0x6f,
0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x2e, 0x11, 0xc0, 0xe9, 0xcb, 0x00, 0x00, 0x00,
0x88, 0xb4, 0x52, 0x13, 0x23, 0x17, 0x17, 0x42, 0x54, 0x48, 0x8a, 0x8b, 0x23, 0x34, 0x2f, 0x25,
0xb5, 0x28, 0x27, 0xb1, 0x52, 0x82, 0x51, 0x81, 0x51, 0x83, 0x27, 0x08, 0xce, 0x17, 0x92, 0xe1,
0xe2, 0x0c, 0xce, 0x4c, 0xcf, 0x4b, 0x2c, 0x29, 0x2d, 0x4a, 0x95, 0x60, 0x02, 0x4b, 0x22, 0x04,
0x84, 0x24, 0xb8, 0xd8, 0xfd, 0xcb, 0x20, 0x1a, 0x99, 0xc1, 0x72, 0x30, 0xae, 0x90, 0x02, 0x17,
0x77, 0x48, 0x51, 0x62, 0x5e, 0x71, 0x62, 0x72, 0x49, 0x66, 0x7e, 0x9e, 0x04, 0x0b, 0x58, 0x16,
0x59, 0xc8, 0x49, 0xe6, 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63,
0x9c, 0xf0, 0x58, 0x8e, 0xe1, 0xc2, 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5, 0x18, 0xa2, 0x98, 0x0a,
0x92, 0x92, 0xd8, 0xc0, 0x1e, 0x34, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xe0, 0x0a, 0xbb, 0x89,
0xee, 0x00, 0x00, 0x00,
}
func (m *Peers) Marshal() (dAtA []byte, err error) {
......@@ -205,6 +215,13 @@ func (m *BzzAddress) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if len(m.Transaction) > 0 {
i -= len(m.Transaction)
copy(dAtA[i:], m.Transaction)
i = encodeVarintHive(dAtA, i, uint64(len(m.Transaction)))
i--
dAtA[i] = 0x22
}
if len(m.Overlay) > 0 {
i -= len(m.Overlay)
copy(dAtA[i:], m.Overlay)
......@@ -273,6 +290,10 @@ func (m *BzzAddress) Size() (n int) {
if l > 0 {
n += 1 + l + sovHive(uint64(l))
}
l = len(m.Transaction)
if l > 0 {
n += 1 + l + sovHive(uint64(l))
}
return n
}
......@@ -500,6 +521,40 @@ func (m *BzzAddress) Unmarshal(dAtA []byte) error {
m.Overlay = []byte{}
}
iNdEx = postIndex
case 4:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Transaction", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowHive
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthHive
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthHive
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Transaction = append(m.Transaction[:0], dAtA[iNdEx:postIndex]...)
if m.Transaction == nil {
m.Transaction = []byte{}
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipHive(dAtA[iNdEx:])
......
......@@ -16,4 +16,5 @@ message BzzAddress {
bytes Underlay = 1;
bytes Signature = 2;
bytes Overlay = 3;
bytes Transaction = 4;
}
......@@ -6,9 +6,11 @@ package node
import (
"context"
"encoding/hex"
"errors"
"fmt"
"math/big"
"strings"
"time"
"github.com/ethereum/go-ethereum/common"
......@@ -39,7 +41,7 @@ func InitChain(
stateStore storage.StateStorer,
endpoint string,
signer crypto.Signer,
blocktime uint64,
pollingInterval time.Duration,
) (*ethclient.Client, common.Address, int64, transaction.Monitor, transaction.Service, error) {
backend, err := ethclient.Dial(endpoint)
if err != nil {
......@@ -52,7 +54,6 @@ func InitChain(
return nil, common.Address{}, 0, nil, nil, fmt.Errorf("get chain id: %w", err)
}
pollingInterval := time.Duration(blocktime) * time.Second
overlayEthAddress, err := signer.EthereumAddress()
if err != nil {
return nil, common.Address{}, 0, nil, nil, fmt.Errorf("eth address: %w", err)
......@@ -257,3 +258,65 @@ func InitSwap(
return swapService, priceOracle, nil
}
func GetTxHash(stateStore storage.StateStorer, logger logging.Logger, trxString string) ([]byte, error) {
if trxString != "" {
txHashTrimmed := strings.TrimPrefix(trxString, "0x")
if len(txHashTrimmed) != 64 {
return nil, errors.New("invalid length")
}
txHash, err := hex.DecodeString(txHashTrimmed)
if err != nil {
return nil, err
}
logger.Infof("using the provided transaction hash %x", txHash)
return txHash, nil
}
var txHash common.Hash
key := chequebook.ChequebookDeploymentKey
if err := stateStore.Get(key, &txHash); err != nil {
if errors.Is(err, storage.ErrNotFound) {
return nil, errors.New("chequebook deployment transaction hash not found, please specify the transaction hash manually")
}
return nil, err
}
logger.Infof("using the chequebook transaction hash %x", txHash)
return txHash.Bytes(), nil
}
func GetTxNextBlock(ctx context.Context, logger logging.Logger, backend transaction.Backend, monitor transaction.Monitor, duration time.Duration, trx []byte, blockHash string) ([]byte, error) {
if blockHash != "" {
blockHashTrimmed := strings.TrimPrefix(blockHash, "0x")
if len(blockHashTrimmed) != 64 {
return nil, errors.New("invalid length")
}
blockHash, err := hex.DecodeString(blockHashTrimmed)
if err != nil {
return nil, err
}
logger.Infof("using the provided block hash %x", blockHash)
return blockHash, nil
}
// if not found in statestore, fetch from chain
tx, err := backend.TransactionReceipt(ctx, common.BytesToHash(trx))
if err != nil {
return nil, err
}
block, err := transaction.WaitBlock(ctx, backend, duration, big.NewInt(0).Add(tx.BlockNumber, big.NewInt(1)))
if err != nil {
return nil, err
}
hash := block.Hash()
hashBytes := hash.Bytes()
logger.Infof("using the next block hash from the blockchain %x", hashBytes)
return hashBytes, nil
}
......@@ -10,7 +10,6 @@ package node
import (
"context"
"crypto/ecdsa"
"encoding/hex"
"errors"
"fmt"
"io"
......@@ -20,7 +19,6 @@ import (
"net/http"
"os"
"path/filepath"
"strings"
"sync"
"syscall"
"time"
......@@ -66,7 +64,6 @@ import (
"github.com/ethersphere/bee/pkg/shed"
"github.com/ethersphere/bee/pkg/steward"
"github.com/ethersphere/bee/pkg/storage"
"github.com/ethersphere/bee/pkg/swarm"
"github.com/ethersphere/bee/pkg/tags"
"github.com/ethersphere/bee/pkg/topology"
"github.com/ethersphere/bee/pkg/topology/kademlia"
......@@ -146,6 +143,7 @@ type Options struct {
SwapEnable bool
FullNodeMode bool
Transaction string
BlockHash string
PostageContractAddress string
PriceOracleAddress string
BlockTime uint64
......@@ -158,7 +156,7 @@ const (
basePrice = 10000
)
func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey, signer crypto.Signer, networkID uint64, logger logging.Logger, libp2pPrivateKey, pssPrivateKey *ecdsa.PrivateKey, o Options) (b *Bee, err error) {
func NewBee(addr string, publicKey *ecdsa.PublicKey, signer crypto.Signer, networkID uint64, logger logging.Logger, libp2pPrivateKey, pssPrivateKey *ecdsa.PrivateKey, o *Options) (b *Bee, err error) {
tracer, tracerCloser, err := tracing.NewTracer(&tracing.Options{
Enabled: o.TracingEnabled,
Endpoint: o.TracingEndpoint,
......@@ -190,6 +188,14 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey,
tracerCloser: tracerCloser,
}
stateStore, err := InitStateStore(logger, o.DataDir)
if err != nil {
return nil, err
}
b.stateStoreCloser = stateStore
addressbook := addressbook.New(stateStore)
var debugAPIService *debugapi.Service
if o.DebugAPIAddr != "" {
overlayEthAddress, err := signer.EthereumAddress()
......@@ -197,7 +203,7 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey,
return nil, fmt.Errorf("eth address: %w", err)
}
// set up basic debug api endpoints for debugging and /health endpoint
debugAPIService = debugapi.New(swarmAddress, publicKey, pssPrivateKey.PublicKey, overlayEthAddress, logger, tracer, o.CORSAllowedOrigins)
debugAPIService = debugapi.New(*publicKey, pssPrivateKey.PublicKey, overlayEthAddress, logger, tracer, o.CORSAllowedOrigins)
debugAPIListener, err := net.Listen("tcp", o.DebugAPIAddr)
if err != nil {
......@@ -223,19 +229,6 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey,
b.debugAPIServer = debugAPIServer
}
stateStore, err := InitStateStore(logger, o.DataDir)
if err != nil {
return nil, err
}
b.stateStoreCloser = stateStore
err = CheckOverlayWithStore(swarmAddress, stateStore)
if err != nil {
return nil, err
}
addressbook := addressbook.New(stateStore)
var (
swapBackend *ethclient.Client
overlayEthAddress common.Address
......@@ -246,6 +239,7 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey,
chequebookService chequebook.Service
chequeStore chequebook.ChequeStore
cashoutService chequebook.CashoutService
pollingInterval = time.Duration(o.BlockTime) * time.Second
)
if !o.Standalone {
swapBackend, overlayEthAddress, chainID, transactionMonitor, transactionService, err = InitChain(
......@@ -254,7 +248,7 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey,
stateStore,
o.SwapEndpoint,
signer,
o.BlockTime,
pollingInterval,
)
if err != nil {
return nil, fmt.Errorf("init chain: %w", err)
......@@ -308,13 +302,35 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey,
)
}
lightNodes := lightnode.NewContainer(swarmAddress)
pubKey, _ := signer.PublicKey()
if err != nil {
return nil, err
}
var (
blockHash []byte
txHash []byte
)
txHash, err := getTxHash(stateStore, logger, o)
txHash, err = GetTxHash(stateStore, logger, o.Transaction)
if err != nil {
return nil, fmt.Errorf("invalid transaction hash: %w", err)
}
blockHash, err = GetTxNextBlock(p2pCtx, logger, swapBackend, transactionMonitor, pollingInterval, txHash, o.BlockHash)
if err != nil {
return nil, fmt.Errorf("invalid block hash: %w", err)
}
swarmAddress, err := crypto.NewOverlayAddress(*pubKey, networkID, blockHash)
err = CheckOverlayWithStore(swarmAddress, stateStore)
if err != nil {
return nil, err
}
lightNodes := lightnode.NewContainer(swarmAddress)
senderMatcher := transaction.NewMatcher(swapBackend, types.NewEIP155Signer(big.NewInt(chainID)))
p2ps, err := libp2p.New(p2pCtx, signer, networkID, swarmAddress, addr, addressbook, stateStore, lightNodes, senderMatcher, logger, tracer, libp2p.Options{
......@@ -583,7 +599,7 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey,
pinningService := pinning.NewService(storer, stateStore, traversalService)
pushSyncProtocol := pushsync.New(swarmAddress, p2ps, storer, kad, tagService, o.FullNodeMode, pssService.TryUnwrap, validStamp, logger, acc, pricer, signer, tracer, warmupTime)
pushSyncProtocol := pushsync.New(swarmAddress, blockHash, p2ps, storer, kad, tagService, o.FullNodeMode, pssService.TryUnwrap, validStamp, logger, acc, pricer, signer, tracer, warmupTime)
// set the pushSyncer in the PSS
pssService.SetPushSyncer(pushSyncProtocol)
......@@ -719,7 +735,7 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey,
}
// inject dependencies and configure full debug api http path routes
debugAPIService.Configure(p2ps, pingPong, kad, lightNodes, storer, tagService, acc, pseudosettleService, o.SwapEnable, swapService, chequebookService, batchStore, transactionService)
debugAPIService.Configure(swarmAddress, p2ps, pingPong, kad, lightNodes, storer, tagService, acc, pseudosettleService, o.SwapEnable, swapService, chequebookService, batchStore, transactionService)
}
if err := kad.Start(p2pCtx); err != nil {
......@@ -849,37 +865,6 @@ func (b *Bee) Shutdown(ctx context.Context) error {
return mErr
}
func getTxHash(stateStore storage.StateStorer, logger logging.Logger, o Options) ([]byte, error) {
if o.Standalone {
return nil, nil // in standalone mode tx hash is not used
}
if o.Transaction != "" {
txHashTrimmed := strings.TrimPrefix(o.Transaction, "0x")
if len(txHashTrimmed) != 64 {
return nil, errors.New("invalid length")
}
txHash, err := hex.DecodeString(txHashTrimmed)
if err != nil {
return nil, err
}
logger.Infof("using the provided transaction hash %x", txHash)
return txHash, nil
}
var txHash common.Hash
key := chequebook.ChequebookDeploymentKey
if err := stateStore.Get(key, &txHash); err != nil {
if errors.Is(err, storage.ErrNotFound) {
return nil, errors.New("chequebook deployment transaction hash not found. Please specify the transaction hash manually.")
}
return nil, err
}
logger.Infof("using the chequebook transaction hash %x", txHash)
return txHash.Bytes(), nil
}
// pidKiller is used to issue a forced shut down of the node from sub modules. The issue with using the
// node's Shutdown method is that it only shuts down the node and does not exit the start process
// which is waiting on the os.Signals. This is not desirable, but currently bee node cannot handle
......
......@@ -50,9 +50,6 @@ var (
// ErrInvalidSyn is returned if observable address in ack is not a valid..
ErrInvalidSyn = errors.New("invalid syn")
// ErrAddressNotFound is returned if observable address in ack is not a valid..
ErrAddressNotFound = errors.New("address not found")
// ErrWelcomeMessageLength is returned if the welcome message is longer than the maximum length
ErrWelcomeMessageLength = fmt.Errorf("handshake welcome message longer than maximum of %d characters", MaxWelcomeMessageLength)
)
......@@ -63,7 +60,7 @@ type AdvertisableAddressResolver interface {
}
type SenderMatcher interface {
Matches(ctx context.Context, tx []byte, networkID uint64, senderOverlay swarm.Address) (bool, error)
Matches(ctx context.Context, tx []byte, networkID uint64, senderOverlay swarm.Address) ([]byte, error)
}
// Service can perform initiate or handle a handshake between peers.
......@@ -147,11 +144,6 @@ func (s *Service) Handshake(ctx context.Context, stream p2p.Stream, peerMultiadd
return nil, fmt.Errorf("read synack message: %w", err)
}
remoteBzzAddress, err := s.parseCheckAck(resp.Ack)
if err != nil {
return nil, err
}
observedUnderlay, err := ma.NewMultiaddrBytes(resp.Syn.ObservedUnderlay)
if err != nil {
return nil, ErrInvalidSyn
......@@ -162,7 +154,7 @@ func (s *Service) Handshake(ctx context.Context, stream p2p.Stream, peerMultiadd
return nil, err
}
bzzAddress, err := bzz.NewAddress(s.signer, advertisableUnderlay, s.overlay, s.networkID)
bzzAddress, err := bzz.NewAddress(s.signer, advertisableUnderlay, s.overlay, s.networkID, s.transaction)
if err != nil {
return nil, err
}
......@@ -172,6 +164,18 @@ func (s *Service) Handshake(ctx context.Context, stream p2p.Stream, peerMultiadd
return nil, err
}
overlay := swarm.NewAddress(resp.Ack.Address.Overlay)
blockHash, err := s.senderMatcher.Matches(ctx, resp.Ack.Transaction, s.networkID, overlay)
if err != nil {
return nil, fmt.Errorf("overlay %v verification failed: %w", overlay, err)
}
remoteBzzAddress, err := s.parseCheckAck(resp.Ack, blockHash)
if err != nil {
return nil, err
}
// Synced read:
welcomeMessage := s.GetWelcomeMessage()
if err := w.WriteMsgWithContext(ctx, &pb.Ack{
......@@ -238,7 +242,7 @@ func (s *Service) Handle(ctx context.Context, stream p2p.Stream, remoteMultiaddr
return nil, err
}
bzzAddress, err := bzz.NewAddress(s.signer, advertisableUnderlay, s.overlay, s.networkID)
bzzAddress, err := bzz.NewAddress(s.signer, advertisableUnderlay, s.overlay, s.networkID, s.transaction)
if err != nil {
return nil, err
}
......@@ -274,23 +278,21 @@ func (s *Service) Handle(ctx context.Context, stream p2p.Stream, remoteMultiaddr
return nil, fmt.Errorf("read ack message: %w", err)
}
remoteBzzAddress, err := s.parseCheckAck(&ack)
if err != nil {
return nil, err
}
overlay := swarm.NewAddress(ack.Address.Overlay)
s.logger.Tracef("handshake finished for peer (inbound) %s", remoteBzzAddress.Overlay.String())
if len(ack.WelcomeMessage) > 0 {
s.logger.Infof("greeting \"%s\" from peer: %s", ack.WelcomeMessage, remoteBzzAddress.Overlay.String())
blockHash, err := s.senderMatcher.Matches(ctx, ack.Transaction, s.networkID, overlay)
if err != nil {
return nil, fmt.Errorf("overlay %v verification failed: %w", overlay, err)
}
matchesSender, err := s.senderMatcher.Matches(ctx, ack.Transaction, s.networkID, remoteBzzAddress.Overlay)
remoteBzzAddress, err := s.parseCheckAck(&ack, blockHash)
if err != nil {
return nil, err
}
if !matchesSender {
return nil, fmt.Errorf("given address is not registered on Ethereum: %v: %w", remoteBzzAddress.Overlay, ErrAddressNotFound)
s.logger.Tracef("handshake finished for peer (inbound) %s", remoteBzzAddress.Overlay.String())
if len(ack.WelcomeMessage) > 0 {
s.logger.Infof("greeting \"%s\" from peer: %s", ack.WelcomeMessage, remoteBzzAddress.Overlay.String())
}
return &Info{
......@@ -324,12 +326,12 @@ func buildFullMA(addr ma.Multiaddr, peerID libp2ppeer.ID) (ma.Multiaddr, error)
return ma.NewMultiaddr(fmt.Sprintf("%s/p2p/%s", addr.String(), peerID.Pretty()))
}
func (s *Service) parseCheckAck(ack *pb.Ack) (*bzz.Address, error) {
func (s *Service) parseCheckAck(ack *pb.Ack, blockHash []byte) (*bzz.Address, error) {
if ack.NetworkID != s.networkID {
return nil, ErrNetworkIDIncompatible
}
bzzAddress, err := bzz.ParseAddress(ack.Address.Underlay, ack.Address.Overlay, ack.Address.Signature, s.networkID)
bzzAddress, err := bzz.ParseAddress(ack.Address.Underlay, ack.Address.Overlay, ack.Address.Signature, ack.Transaction, blockHash, s.networkID)
if err != nil {
return nil, ErrInvalidAck
}
......
......@@ -12,6 +12,7 @@ import (
"io/ioutil"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethersphere/bee/pkg/bzz"
"github.com/ethersphere/bee/pkg/crypto"
"github.com/ethersphere/bee/pkg/logging"
......@@ -63,21 +64,24 @@ func TestHandshake(t *testing.T) {
t.Fatal(err)
}
trxHash := common.HexToHash("0x1").Bytes()
blockhash := common.HexToHash("0x2").Bytes()
signer1 := crypto.NewDefaultSigner(privateKey1)
signer2 := crypto.NewDefaultSigner(privateKey2)
addr, err := crypto.NewOverlayAddress(privateKey1.PublicKey, networkID)
addr, err := crypto.NewOverlayAddress(privateKey1.PublicKey, networkID, blockhash)
if err != nil {
t.Fatal(err)
}
node1BzzAddress, err := bzz.NewAddress(signer1, node1ma, addr, networkID)
node1BzzAddress, err := bzz.NewAddress(signer1, node1ma, addr, networkID, trxHash)
if err != nil {
t.Fatal(err)
}
addr2, err := crypto.NewOverlayAddress(privateKey2.PublicKey, networkID)
addr2, err := crypto.NewOverlayAddress(privateKey2.PublicKey, networkID, blockhash)
if err != nil {
t.Fatal(err)
}
node2BzzAddress, err := bzz.NewAddress(signer2, node2ma, addr2, networkID)
node2BzzAddress, err := bzz.NewAddress(signer2, node2ma, addr2, networkID, trxHash)
if err != nil {
t.Fatal(err)
}
......@@ -92,9 +96,10 @@ func TestHandshake(t *testing.T) {
}
aaddresser := &AdvertisableAddresserMock{}
senderMatcher := &MockSenderMatcher{v: true}
handshakeService, err := handshake.New(signer1, aaddresser, senderMatcher, node1Info.BzzAddress.Overlay, networkID, true, nil, testWelcomeMessage, logger)
senderMatcher := &MockSenderMatcher{v: true, blockHash: blockhash}
handshakeService, err := handshake.New(signer1, aaddresser, senderMatcher, node1Info.BzzAddress.Overlay, networkID, true, trxHash, testWelcomeMessage, logger)
if err != nil {
t.Fatal(err)
}
......@@ -118,6 +123,7 @@ func TestHandshake(t *testing.T) {
},
NetworkID: networkID,
FullNode: true,
Transaction: trxHash,
WelcomeMessage: testWelcomeMessage,
},
}); err != nil {
......@@ -370,7 +376,7 @@ func TestHandshake(t *testing.T) {
})
t.Run("Handle - OK", func(t *testing.T) {
handshakeService, err := handshake.New(signer1, aaddresser, senderMatcher, node1Info.BzzAddress.Overlay, networkID, true, nil, "", logger)
handshakeService, err := handshake.New(signer1, aaddresser, senderMatcher, node1Info.BzzAddress.Overlay, networkID, true, trxHash, "", logger)
if err != nil {
t.Fatal(err)
}
......@@ -392,8 +398,9 @@ func TestHandshake(t *testing.T) {
Overlay: node2BzzAddress.Overlay.Bytes(),
Signature: node2BzzAddress.Signature,
},
NetworkID: networkID,
FullNode: true,
NetworkID: networkID,
Transaction: trxHash,
FullNode: true,
}); err != nil {
t.Fatal(err)
}
......@@ -415,7 +422,7 @@ func TestHandshake(t *testing.T) {
t.Fatalf("got bad syn")
}
bzzAddress, err := bzz.ParseAddress(got.Ack.Address.Underlay, got.Ack.Address.Overlay, got.Ack.Address.Signature, got.Ack.NetworkID)
bzzAddress, err := bzz.ParseAddress(got.Ack.Address.Underlay, got.Ack.Address.Overlay, got.Ack.Address.Signature, got.Ack.Transaction, blockhash, got.Ack.NetworkID)
if err != nil {
t.Fatal(err)
}
......@@ -541,7 +548,7 @@ func TestHandshake(t *testing.T) {
})
t.Run("Handle - duplicate handshake", func(t *testing.T) {
handshakeService, err := handshake.New(signer1, aaddresser, senderMatcher, node1Info.BzzAddress.Overlay, networkID, true, nil, "", logger)
handshakeService, err := handshake.New(signer1, aaddresser, senderMatcher, node1Info.BzzAddress.Overlay, networkID, true, trxHash, "", logger)
if err != nil {
t.Fatal(err)
}
......@@ -563,8 +570,9 @@ func TestHandshake(t *testing.T) {
Overlay: node2BzzAddress.Overlay.Bytes(),
Signature: node2BzzAddress.Signature,
},
NetworkID: networkID,
FullNode: true,
NetworkID: networkID,
Transaction: trxHash,
FullNode: true,
}); err != nil {
t.Fatal(err)
}
......@@ -585,8 +593,7 @@ func TestHandshake(t *testing.T) {
if !bytes.Equal(got.Syn.ObservedUnderlay, node2maBinary) {
t.Fatalf("got bad syn")
}
bzzAddress, err := bzz.ParseAddress(got.Ack.Address.Underlay, got.Ack.Address.Overlay, got.Ack.Address.Signature, got.Ack.NetworkID)
bzzAddress, err := bzz.ParseAddress(got.Ack.Address.Underlay, got.Ack.Address.Overlay, got.Ack.Address.Signature, got.Ack.Transaction, blockhash, got.Ack.NetworkID)
if err != nil {
t.Fatal(err)
}
......@@ -638,9 +645,9 @@ func TestHandshake(t *testing.T) {
})
t.Run("Handle - transaction is not on the blockchain", func(t *testing.T) {
sbMock := &MockSenderMatcher{v: false}
sbMock := &MockSenderMatcher{v: false, blockHash: blockhash}
handshakeService, err := handshake.New(signer1, aaddresser, sbMock, node1Info.BzzAddress.Overlay, networkID, true, []byte("0xff"), "", logger)
handshakeService, err := handshake.New(signer1, aaddresser, sbMock, node1Info.BzzAddress.Overlay, networkID, true, trxHash, "", logger)
if err != nil {
t.Fatal(err)
}
......@@ -662,15 +669,16 @@ func TestHandshake(t *testing.T) {
Overlay: node2BzzAddress.Overlay.Bytes(),
Signature: node2BzzAddress.Signature,
},
NetworkID: networkID,
FullNode: true,
NetworkID: networkID,
FullNode: true,
Transaction: trxHash,
}); err != nil {
t.Fatal(err)
}
_, err = handshakeService.Handle(context.Background(), stream1, node2AddrInfo.Addrs[0], node2AddrInfo.ID)
if !errors.Is(err, handshake.ErrAddressNotFound) {
t.Fatalf("expected error %v, got %v", handshake.ErrAddressNotFound, err)
if err == nil {
t.Fatalf("expected error, got nil")
}
})
......@@ -734,9 +742,15 @@ func (a *AdvertisableAddresserMock) Resolve(observedAdddress ma.Multiaddr) (ma.M
}
type MockSenderMatcher struct {
v bool
v bool
blockHash []byte
}
func (m MockSenderMatcher) Matches(context.Context, []byte, uint64, swarm.Address) (bool, error) {
return m.v, nil
func (m MockSenderMatcher) Matches(context.Context, []byte, uint64, swarm.Address) ([]byte, error) {
if m.v {
return m.blockHash, nil
}
return nil, errors.New("")
}
......@@ -13,7 +13,7 @@ import (
"testing"
"time"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common"
"github.com/ethersphere/bee/pkg/addressbook"
"github.com/ethersphere/bee/pkg/crypto"
"github.com/ethersphere/bee/pkg/logging"
......@@ -43,7 +43,10 @@ func newService(t *testing.T, networkID uint64, o libp2pServiceOpts) (s *libp2p.
t.Fatal(err)
}
overlay, err = crypto.NewOverlayAddress(swarmKey.PublicKey, networkID)
trx := common.HexToHash("0x1").Bytes()
blockHash := common.HexToHash("0x2").Bytes()
overlay, err = crypto.NewOverlayAddress(swarmKey.PublicKey, networkID, blockHash)
if err != nil {
t.Fatal(err)
}
......@@ -74,9 +77,11 @@ func newService(t *testing.T, networkID uint64, o libp2pServiceOpts) (s *libp2p.
o.lightNodes = lightnode.NewContainer(overlay)
}
opts := o.libp2pOpts
opts.Transaction = []byte(hexutil.EncodeUint64(o.PrivateKey.Y.Uint64()))
opts.Transaction = trx
senderMatcher := &MockSenderMatcher{}
senderMatcher := &MockSenderMatcher{
BlockHash: blockHash,
}
s, err = libp2p.New(ctx, crypto.NewDefaultSigner(swarmKey), networkID, overlay, addr, o.Addressbook, statestore, o.lightNodes, senderMatcher, o.Logger, nil, opts)
if err != nil {
......@@ -160,8 +165,10 @@ func serviceUnderlayAddress(t *testing.T, s *libp2p.Service) multiaddr.Multiaddr
return addrs[0]
}
type MockSenderMatcher struct{}
type MockSenderMatcher struct {
BlockHash []byte
}
func (m MockSenderMatcher) Matches(context.Context, []byte, uint64, swarm.Address) (bool, error) {
return true, nil
func (m MockSenderMatcher) Matches(context.Context, []byte, uint64, swarm.Address) ([]byte, error) {
return m.BlockHash, nil
}
......@@ -205,7 +205,7 @@ LOOP:
return
}
storerPeer, err = crypto.NewOverlayAddress(*publicKey, s.networkID)
storerPeer, err = crypto.NewOverlayAddress(*publicKey, s.networkID, receipt.BlockHash)
if err != nil {
err = fmt.Errorf("pusher: receipt storer address: %w", err)
return
......
......@@ -13,6 +13,7 @@ import (
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethersphere/bee/pkg/crypto"
statestore "github.com/ethersphere/bee/pkg/statestore/mock"
......@@ -30,6 +31,7 @@ import (
// no of times to retry to see if we have received response from pushsync
var noOfRetries = 20
var block = common.HexToHash("0x1").Bytes()
// Wrap the actual storer to intercept the modeSet that the pusher will call when a valid receipt is received
type Store struct {
......@@ -84,6 +86,7 @@ func TestSendChunkToSyncWithTag(t *testing.T) {
receipt := &pushsync.Receipt{
Address: swarm.NewAddress(chunk.Address().Bytes()),
Signature: signature,
BlockHash: block,
}
return receipt, nil
})
......@@ -140,6 +143,7 @@ func TestSendChunkToPushSyncWithoutTag(t *testing.T) {
receipt := &pushsync.Receipt{
Address: swarm.NewAddress(chunk.Address().Bytes()),
Signature: signature,
BlockHash: block,
}
return receipt, nil
})
......@@ -225,6 +229,7 @@ func TestSendChunkAndTimeoutinReceivingReceipt(t *testing.T) {
receipt := &pushsync.Receipt{
Address: swarm.NewAddress(chunk.Address().Bytes()),
Signature: signature,
BlockHash: block,
}
return receipt, nil
})
......@@ -280,6 +285,7 @@ func TestPusherClose(t *testing.T) {
receipt := &pushsync.Receipt{
Address: swarm.NewAddress(chunk.Address().Bytes()),
Signature: signature,
BlockHash: block,
}
return receipt, nil
})
......@@ -383,6 +389,7 @@ func TestPusherRetryShallow(t *testing.T) {
receipt := &pushsync.Receipt{
Address: swarm.NewAddress(chunk.Address().Bytes()),
Signature: signature,
BlockHash: block,
}
return receipt, nil
})
......@@ -457,7 +464,3 @@ func checkIfModeSet(addr swarm.Address, mode storage.ModeSet, storer *Store) err
}
return nil
}
// To avoid timeout during race testing
// cd pkg/pusher
// go test -race -count 1000 -timeout 60m .
......@@ -85,6 +85,7 @@ func (m *Delivery) GetStamp() []byte {
type Receipt struct {
Address []byte `protobuf:"bytes,1,opt,name=Address,proto3" json:"Address,omitempty"`
Signature []byte `protobuf:"bytes,2,opt,name=Signature,proto3" json:"Signature,omitempty"`
BlockHash []byte `protobuf:"bytes,3,opt,name=BlockHash,proto3" json:"BlockHash,omitempty"`
}
func (m *Receipt) Reset() { *m = Receipt{} }
......@@ -134,6 +135,13 @@ func (m *Receipt) GetSignature() []byte {
return nil
}
func (m *Receipt) GetBlockHash() []byte {
if m != nil {
return m.BlockHash
}
return nil
}
func init() {
proto.RegisterType((*Delivery)(nil), "pushsync.Delivery")
proto.RegisterType((*Receipt)(nil), "pushsync.Receipt")
......@@ -142,18 +150,19 @@ func init() {
func init() { proto.RegisterFile("pushsync.proto", fileDescriptor_723cf31bfc02bfd6) }
var fileDescriptor_723cf31bfc02bfd6 = []byte{
// 170 bytes of a gzipped FileDescriptorProto
// 185 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2b, 0x28, 0x2d, 0xce,
0x28, 0xae, 0xcc, 0x4b, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x80, 0xf1, 0x95, 0xfc,
0xb8, 0x38, 0x5c, 0x52, 0x73, 0x32, 0xcb, 0x52, 0x8b, 0x2a, 0x85, 0x24, 0xb8, 0xd8, 0x1d, 0x53,
0x52, 0x8a, 0x52, 0x8b, 0x8b, 0x25, 0x18, 0x15, 0x18, 0x35, 0x78, 0x82, 0x60, 0x5c, 0x21, 0x21,
0x2e, 0x16, 0x97, 0xc4, 0x92, 0x44, 0x09, 0x26, 0xb0, 0x30, 0x98, 0x2d, 0x24, 0xc2, 0xc5, 0x1a,
0x5c, 0x92, 0x98, 0x5b, 0x20, 0xc1, 0x0c, 0x16, 0x84, 0x70, 0x94, 0x1c, 0xb9, 0xd8, 0x83, 0x52,
0x5c, 0x92, 0x98, 0x5b, 0x20, 0xc1, 0x0c, 0x16, 0x84, 0x70, 0x94, 0xe2, 0xb9, 0xd8, 0x83, 0x52,
0x93, 0x53, 0x33, 0x0b, 0x4a, 0xf0, 0x18, 0x27, 0xc3, 0xc5, 0x19, 0x9c, 0x99, 0x9e, 0x97, 0x58,
0x52, 0x5a, 0x94, 0x0a, 0x35, 0x13, 0x21, 0xe0, 0x24, 0x73, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47,
0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d,
0xc7, 0x72, 0x0c, 0x51, 0x4c, 0x05, 0x49, 0x49, 0x6c, 0x60, 0x1f, 0x18, 0x03, 0x02, 0x00, 0x00,
0xff, 0xff, 0xbb, 0xdf, 0x60, 0x63, 0xd3, 0x00, 0x00, 0x00,
0x52, 0x5a, 0x94, 0x0a, 0x35, 0x13, 0x21, 0x00, 0x92, 0x75, 0xca, 0xc9, 0x4f, 0xce, 0xf6, 0x48,
0x2c, 0xce, 0x80, 0x1a, 0x8e, 0x10, 0x70, 0x92, 0x39, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39,
0xc6, 0x07, 0x8f, 0xe4, 0x18, 0x27, 0x3c, 0x96, 0x63, 0xb8, 0xf0, 0x58, 0x8e, 0xe1, 0xc6, 0x63,
0x39, 0x86, 0x28, 0xa6, 0x82, 0xa4, 0x24, 0x36, 0xb0, 0xff, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff,
0xff, 0xd8, 0x59, 0x25, 0xda, 0xf1, 0x00, 0x00, 0x00,
}
func (m *Delivery) Marshal() (dAtA []byte, err error) {
......@@ -220,6 +229,13 @@ func (m *Receipt) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if len(m.BlockHash) > 0 {
i -= len(m.BlockHash)
copy(dAtA[i:], m.BlockHash)
i = encodeVarintPushsync(dAtA, i, uint64(len(m.BlockHash)))
i--
dAtA[i] = 0x1a
}
if len(m.Signature) > 0 {
i -= len(m.Signature)
copy(dAtA[i:], m.Signature)
......@@ -283,6 +299,10 @@ func (m *Receipt) Size() (n int) {
if l > 0 {
n += 1 + l + sovPushsync(uint64(l))
}
l = len(m.BlockHash)
if l > 0 {
n += 1 + l + sovPushsync(uint64(l))
}
return n
}
......@@ -544,6 +564,40 @@ func (m *Receipt) Unmarshal(dAtA []byte) error {
m.Signature = []byte{}
}
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field BlockHash", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPushsync
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthPushsync
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthPushsync
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.BlockHash = append(m.BlockHash[:0], dAtA[iNdEx:postIndex]...)
if m.BlockHash == nil {
m.BlockHash = []byte{}
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipPushsync(dAtA[iNdEx:])
......
......@@ -17,4 +17,5 @@ message Delivery {
message Receipt {
bytes Address = 1;
bytes Signature = 2;
bytes BlockHash = 3;
}
......@@ -55,10 +55,12 @@ type PushSyncer interface {
type Receipt struct {
Address swarm.Address
Signature []byte
BlockHash []byte
}
type PushSync struct {
address swarm.Address
blockHash []byte
streamer p2p.StreamerDisconnecter
storer storage.Putter
topologyDriver topology.Driver
......@@ -80,9 +82,10 @@ var defaultTTL = 20 * time.Second // request time to live
var timeToWaitForPushsyncToNeighbor = 3 * time.Second // time to wait to get a receipt for a chunk
var nPeersToPushsync = 3 // number of peers to replicate to as receipt is sent upstream
func New(address swarm.Address, streamer p2p.StreamerDisconnecter, storer storage.Putter, topology topology.Driver, tagger *tags.Tags, isFullNode bool, unwrap func(swarm.Chunk), validStamp func(swarm.Chunk, []byte) (swarm.Chunk, error), logger logging.Logger, accounting accounting.Interface, pricer pricer.Interface, signer crypto.Signer, tracer *tracing.Tracer, warmupTime time.Duration) *PushSync {
func New(address swarm.Address, blockHash []byte, streamer p2p.StreamerDisconnecter, storer storage.Putter, topology topology.Driver, tagger *tags.Tags, isFullNode bool, unwrap func(swarm.Chunk), validStamp func(swarm.Chunk, []byte) (swarm.Chunk, error), logger logging.Logger, accounting accounting.Interface, pricer pricer.Interface, signer crypto.Signer, tracer *tracing.Tracer, warmupTime time.Duration) *PushSync {
ps := &PushSync{
address: address,
blockHash: blockHash,
streamer: streamer,
storer: storer,
topologyDriver: topology,
......@@ -174,7 +177,7 @@ func (ps *PushSync) handler(ctx context.Context, p p2p.Peer, stream p2p.Stream)
if err != nil {
return fmt.Errorf("receipt signature: %w", err)
}
receipt := pb.Receipt{Address: bytes, Signature: signature}
receipt := pb.Receipt{Address: bytes, Signature: signature, BlockHash: ps.blockHash}
if err := w.WriteMsgWithContext(ctxd, &receipt); err != nil {
return fmt.Errorf("send receipt to peer %s: %w", p.Address.String(), err)
}
......@@ -222,7 +225,7 @@ func (ps *PushSync) handler(ctx context.Context, p p2p.Peer, stream p2p.Stream)
}
defer debit.Cleanup()
receipt := pb.Receipt{Address: chunk.Address().Bytes(), Signature: signature}
receipt := pb.Receipt{Address: chunk.Address().Bytes(), Signature: signature, BlockHash: ps.blockHash}
if err := w.WriteMsgWithContext(ctx, &receipt); err != nil {
return fmt.Errorf("send receipt to peer %s: %w", p.Address.String(), err)
}
......@@ -257,7 +260,8 @@ func (ps *PushSync) PushChunkToClosest(ctx context.Context, ch swarm.Chunk) (*Re
}
return &Receipt{
Address: swarm.NewAddress(r.Address),
Signature: r.Signature}, nil
Signature: r.Signature,
BlockHash: r.BlockHash}, nil
}
type pushResult struct {
......
......@@ -13,6 +13,7 @@ import (
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethersphere/bee/pkg/accounting"
accountingmock "github.com/ethersphere/bee/pkg/accounting/mock"
"github.com/ethersphere/bee/pkg/crypto"
......@@ -39,6 +40,8 @@ const (
fixedPrice = uint64(10)
)
var blockHash = common.HexToHash("0x1")
type pricerParameters struct {
price uint64
peerPrice uint64
......@@ -776,6 +779,10 @@ func TestSignsReceipt(t *testing.T) {
if !bytes.Equal([]byte{1}, receipt.Signature) {
t.Fatal("receipt signature is not present")
}
if !bytes.Equal(blockHash.Bytes(), receipt.BlockHash) {
t.Fatal("receipt block hash do not match")
}
}
func createPushSyncNode(t *testing.T, addr swarm.Address, prices pricerParameters, recorder *streamtest.Recorder, unwrap func(swarm.Chunk), signer crypto.Signer, mockOpts ...mock.Option) (*pushsync.PushSync, *mocks.MockStorer, *tags.Tags, accounting.Interface) {
......@@ -804,7 +811,7 @@ func createPushSyncNodeWithAccounting(t *testing.T, addr swarm.Address, prices p
return ch.WithStamp(postage.NewStamp(nil, nil, nil, nil)), nil
}
return pushsync.New(addr, recorderDisconnecter, storer, mockTopology, mtag, true, unwrap, validStamp, logger, acct, mockPricer, signer, nil, 0), storer, mtag
return pushsync.New(addr, blockHash.Bytes(), recorderDisconnecter, storer, mockTopology, mtag, true, unwrap, validStamp, logger, acct, mockPricer, signer, nil, 0), storer, mtag
}
func waitOnRecordAndTest(t *testing.T, peer swarm.Address, recorder *streamtest.Recorder, add swarm.Address, data []byte) {
......
......@@ -11,7 +11,6 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethersphere/bee/pkg/crypto"
"github.com/ethersphere/bee/pkg/logging"
"github.com/ethersphere/bee/pkg/settlement"
"github.com/ethersphere/bee/pkg/settlement/swap/chequebook"
......@@ -237,13 +236,6 @@ func (s *Service) SettlementsReceived() (map[string]*big.Int, error) {
// Handshake is called by the swap protocol when a handshake is received.
func (s *Service) Handshake(peer swarm.Address, beneficiary common.Address) error {
// check that the overlay address was derived from the beneficiary (implying they have the same private key)
// while this is not strictly necessary for correct functionality we need to ensure no two peers use the same beneficiary
// as long as we enforce this we might not need the handshake message if the p2p layer exposed the overlay public key
expectedOverlay := crypto.NewOverlayFromEthereumAddress(beneficiary[:], s.networkID)
if !expectedOverlay.Equal(peer) {
return ErrWrongBeneficiary
}
storedBeneficiary, known, err := s.addressbook.Beneficiary(peer)
if err != nil {
......
......@@ -527,7 +527,9 @@ func TestHandshake(t *testing.T) {
beneficiary := common.HexToAddress("0xcd")
networkID := uint64(1)
peer := crypto.NewOverlayFromEthereumAddress(beneficiary[:], networkID)
txHash := common.HexToHash("0x1")
peer := crypto.NewOverlayFromEthereumAddress(beneficiary[:], networkID, txHash.Bytes())
var putCalled bool
swapService := swap.New(
......@@ -565,8 +567,9 @@ func TestHandshakeNewPeer(t *testing.T) {
store := mockstore.NewStateStore()
beneficiary := common.HexToAddress("0xcd")
trx := common.HexToHash("0x1")
networkID := uint64(1)
peer := crypto.NewOverlayFromEthereumAddress(beneficiary[:], networkID)
peer := crypto.NewOverlayFromEthereumAddress(beneficiary[:], networkID, trx.Bytes())
var putCalled bool
swapService := swap.New(
......@@ -600,6 +603,9 @@ func TestHandshakeNewPeer(t *testing.T) {
}
func TestHandshakeWrongBeneficiary(t *testing.T) {
t.Skip()
logger := logging.New(ioutil.Discard, 0)
store := mockstore.NewStateStore()
......
......@@ -167,7 +167,6 @@ func (s *Service) init(ctx context.Context, p p2p.Peer) error {
}
beneficiary := common.BytesToAddress(req.Beneficiary)
return s.swap.Handshake(p.Address, beneficiary)
}
......
......@@ -733,7 +733,7 @@ func TestAddressBookPrune(t *testing.T) {
}
defer kad.Close()
nonConnPeer, err := bzz.NewAddress(signer, nonConnectableAddress, test.RandomAddressAt(base, 1), 0)
nonConnPeer, err := bzz.NewAddress(signer, nonConnectableAddress, test.RandomAddressAt(base, 1), 0, nil)
if err != nil {
t.Fatal(err)
}
......@@ -809,7 +809,7 @@ func TestAddressBookQuickPrune(t *testing.T) {
time.Sleep(100 * time.Millisecond)
nonConnPeer, err := bzz.NewAddress(signer, nonConnectableAddress, test.RandomAddressAt(base, 1), 0)
nonConnPeer, err := bzz.NewAddress(signer, nonConnectableAddress, test.RandomAddressAt(base, 1), 0, nil)
if err != nil {
t.Fatal(err)
}
......@@ -1117,7 +1117,7 @@ func TestStart(t *testing.T) {
if err != nil {
t.Fatal(err)
}
bzzAddr, err := bzz.NewAddress(signer, multiaddr, peer, 0)
bzzAddr, err := bzz.NewAddress(signer, multiaddr, peer, 0, nil)
if err != nil {
t.Fatal(err)
}
......@@ -1197,7 +1197,7 @@ func p2pMock(ab addressbook.Interface, signer beeCrypto.Signer, counter, failedC
}
address := test.RandomAddress()
bzzAddr, err := bzz.NewAddress(signer, addr, address, 0)
bzzAddr, err := bzz.NewAddress(signer, addr, address, 0, nil)
if err != nil {
return nil, err
}
......@@ -1232,7 +1232,7 @@ func connectOne(t *testing.T, signer beeCrypto.Signer, k *kademlia.Kad, ab addre
t.Fatal(err)
}
bzzAddr, err := bzz.NewAddress(signer, multiaddr, peer, 0)
bzzAddr, err := bzz.NewAddress(signer, multiaddr, peer, 0, nil)
if err != nil {
t.Fatal(err)
}
......@@ -1253,7 +1253,7 @@ func addOne(t *testing.T, signer beeCrypto.Signer, k *kademlia.Kad, ab addressbo
if err != nil {
t.Fatal(err)
}
bzzAddr, err := bzz.NewAddress(signer, multiaddr, peer, 0)
bzzAddr, err := bzz.NewAddress(signer, multiaddr, peer, 0, nil)
if err != nil {
t.Fatal(err)
}
......
......@@ -6,11 +6,13 @@ package transaction
import (
"context"
"errors"
"fmt"
"math/big"
"strings"
"time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
......@@ -23,6 +25,7 @@ type Backend interface {
TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
TransactionByHash(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error)
BlockNumber(ctx context.Context) (uint64, error)
BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error)
HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
BalanceAt(ctx context.Context, address common.Address, block *big.Int) (*big.Int, error)
NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error)
......@@ -70,6 +73,25 @@ func WaitSynced(ctx context.Context, backend Backend, maxDelay time.Duration) er
}
}
func WaitBlock(ctx context.Context, backend Backend, pollingInterval time.Duration, block *big.Int) (*types.Block, error) {
for {
block, err := backend.BlockByNumber(ctx, block)
if err != nil {
if !errors.Is(err, ethereum.NotFound) {
return nil, err
}
} else {
return block, nil
}
select {
case <-time.After(pollingInterval):
case <-ctx.Done():
return nil, errors.New("context timeout")
}
}
}
// ParseABIUnchecked will parse a valid json abi. Only use this with string constants known to be correct.
func ParseABIUnchecked(json string) abi.ABI {
cabi, err := abi.JSON(strings.NewReader(json))
......
......@@ -24,6 +24,7 @@ type backendMock struct {
pendingNonceAt func(ctx context.Context, account common.Address) (uint64, error)
transactionByHash func(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error)
blockNumber func(ctx context.Context) (uint64, error)
blockByNumber func(ctx context.Context, number *big.Int) (*types.Block, error)
headerByNumber func(ctx context.Context, number *big.Int) (*types.Header, error)
balanceAt func(ctx context.Context, address common.Address, block *big.Int) (*big.Int, error)
nonceAt func(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error)
......@@ -101,6 +102,13 @@ func (m *backendMock) BlockNumber(ctx context.Context) (uint64, error) {
return 0, errors.New("not implemented")
}
func (m *backendMock) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
if m.blockNumber != nil {
return m.blockByNumber(ctx, number)
}
return nil, errors.New("not implemented")
}
func (m *backendMock) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
if m.headerByNumber != nil {
return m.headerByNumber(ctx, number)
......@@ -174,6 +182,12 @@ func WithTransactionByHashFunc(f func(ctx context.Context, txHash common.Hash) (
})
}
func WithBlockByNumberFunc(f func(ctx context.Context, number *big.Int) (*types.Block, error)) Option {
return optionFunc(func(s *backendMock) {
s.blockByNumber = f
})
}
func WithSendTransactionFunc(f func(ctx context.Context, tx *types.Transaction) error) Option {
return optionFunc(func(s *backendMock) {
s.sendTransaction = f
......
......@@ -86,6 +86,10 @@ func (m *simulatedBackend) advanceBlock() {
}
}
func (m *simulatedBackend) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
return nil, errors.New("not implemented")
}
func (m *simulatedBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
return nil, errors.New("not implemented")
}
......
......@@ -116,9 +116,11 @@ func (tm *transactionMonitor) watchPending() {
}
}()
var lastBlock uint64 = 0
var (
lastBlock uint64 = 0
added bool // flag if this iteration was triggered by the watchAdded channel
)
var added bool // flag if this iteration was triggered by the watchAdded channel
for {
added = false
select {
......@@ -153,7 +155,6 @@ func (tm *transactionMonitor) watchPending() {
tm.logger.Tracef("error while checking pending transactions: %v", err)
continue
}
lastBlock = block
}
}
......
......@@ -5,7 +5,9 @@
package monitormock
import (
"context"
"errors"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
......@@ -14,6 +16,7 @@ import (
type transactionMonitorMock struct {
watchTransaction func(txHash common.Hash, nonce uint64) (<-chan types.Receipt, <-chan error, error)
waitBlock func(ctx context.Context, block *big.Int) (*types.Block, error)
}
func (m *transactionMonitorMock) WatchTransaction(txHash common.Hash, nonce uint64) (<-chan types.Receipt, <-chan error, error) {
......@@ -23,6 +26,13 @@ func (m *transactionMonitorMock) WatchTransaction(txHash common.Hash, nonce uint
return nil, nil, errors.New("not implemented")
}
func (m *transactionMonitorMock) WaitBlock(ctx context.Context, block *big.Int) (*types.Block, error) {
if m.watchTransaction != nil {
return m.waitBlock(ctx, block)
}
return nil, errors.New("not implemented")
}
func (m *transactionMonitorMock) Close() error {
return nil
}
......@@ -42,6 +52,12 @@ func WithWatchTransactionFunc(f func(txHash common.Hash, nonce uint64) (<-chan t
})
}
func WithWaitBlockFunc(f func(ctx context.Context, block *big.Int) (*types.Block, error)) Option {
return optionFunc(func(s *transactionMonitorMock) {
s.waitBlock = f
})
}
func New(opts ...Option) transaction.Monitor {
mock := new(transactionMonitorMock)
for _, o := range opts {
......
package transaction
import (
"bytes"
"context"
"errors"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
......@@ -20,6 +22,8 @@ var (
ErrTransactionNotFound = errors.New("transaction not found")
ErrTransactionPending = errors.New("transaction in pending status")
ErrTransactionSenderInvalid = errors.New("invalid transaction sender")
ErrBlockHashMismatch = errors.New("block hash mismatch")
ErrOverlayMismatch = errors.New("overlay mismatch")
)
func NewMatcher(backend Backend, signer types.Signer) *Matcher {
......@@ -29,24 +33,47 @@ func NewMatcher(backend Backend, signer types.Signer) *Matcher {
}
}
func (m Matcher) Matches(ctx context.Context, tx []byte, networkID uint64, senderOverlay swarm.Address) (bool, error) {
func (m *Matcher) Matches(ctx context.Context, tx []byte, networkID uint64, senderOverlay swarm.Address) ([]byte, error) {
incomingTx := common.BytesToHash(tx)
nTx, isPending, err := m.backend.TransactionByHash(ctx, incomingTx)
if err != nil {
return false, fmt.Errorf("%v: %w", err, ErrTransactionNotFound)
return nil, fmt.Errorf("%v: %w", err, ErrTransactionNotFound)
}
if isPending {
return false, ErrTransactionPending
return nil, ErrTransactionPending
}
sender, err := types.Sender(m.signer, nTx)
if err != nil {
return false, fmt.Errorf("%v: %w", err, ErrTransactionSenderInvalid)
return nil, fmt.Errorf("%v: %w", err, ErrTransactionSenderInvalid)
}
receipt, err := m.backend.TransactionReceipt(ctx, incomingTx)
if err != nil {
return nil, err
}
nextBlock, err := m.backend.HeaderByNumber(ctx, big.NewInt(0).Add(receipt.BlockNumber, big.NewInt(1)))
if err != nil {
return nil, err
}
receiptBlockHash := receipt.BlockHash.Bytes()
nextBlockParentHash := nextBlock.ParentHash.Bytes()
nextBlockHash := nextBlock.Hash().Bytes()
if !bytes.Equal(receiptBlockHash, nextBlockParentHash) {
return nil, fmt.Errorf("receipt hash %x does not match block's parent hash %x: %w", receiptBlockHash, nextBlockParentHash, ErrBlockHashMismatch)
}
expectedRemoteBzzAddress := crypto.NewOverlayFromEthereumAddress(sender.Bytes(), networkID)
expectedRemoteBzzAddress := crypto.NewOverlayFromEthereumAddress(sender.Bytes(), networkID, nextBlockHash)
if !expectedRemoteBzzAddress.Equal(senderOverlay) {
return nil, ErrOverlayMismatch
}
return expectedRemoteBzzAddress.Equal(senderOverlay), nil
return nextBlockHash, nil
}
......@@ -21,6 +21,7 @@ func TestMatchesSender(t *testing.T) {
suggestedGasPrice := big.NewInt(2)
estimatedGasLimit := uint64(3)
nonce := uint64(2)
trx := common.HexToAddress("0x1").Bytes()
signedTx := types.NewTransaction(nonce, recipient, value, estimatedGasLimit, suggestedGasPrice, txData)
......@@ -31,7 +32,7 @@ func TestMatchesSender(t *testing.T) {
matcher := transaction.NewMatcher(backendmock.New(txByHash), nil)
_, err := matcher.Matches(context.Background(), []byte("0x123"), 0, swarm.NewAddress([]byte{}))
_, err := matcher.Matches(context.Background(), trx, 0, swarm.NewAddress([]byte{}))
if !errors.Is(err, transaction.ErrTransactionNotFound) {
t.Fatalf("bad error type, want %v, got %v", transaction.ErrTransactionNotFound, err)
}
......@@ -44,7 +45,7 @@ func TestMatchesSender(t *testing.T) {
matcher := transaction.NewMatcher(backendmock.New(txByHash), nil)
_, err := matcher.Matches(context.Background(), []byte("0x123"), 0, swarm.NewAddress([]byte{}))
_, err := matcher.Matches(context.Background(), trx, 0, swarm.NewAddress([]byte{}))
if !errors.Is(err, transaction.ErrTransactionPending) {
t.Fatalf("bad error type, want %v, got %v", transaction.ErrTransactionPending, err)
}
......@@ -58,16 +59,19 @@ func TestMatchesSender(t *testing.T) {
signer := &mockSigner{
err: errors.New("can not sign"),
}
matcher := transaction.NewMatcher(backendmock.New(txByHash), signer)
_, err := matcher.Matches(context.Background(), []byte("0x123"), 0, swarm.NewAddress([]byte{}))
_, err := matcher.Matches(context.Background(), trx, 0, swarm.NewAddress([]byte{}))
if !errors.Is(err, transaction.ErrTransactionSenderInvalid) {
t.Fatalf("bad error type, want %v, got %v", transaction.ErrTransactionSenderInvalid, err)
}
})
t.Run("sender does not match", func(t *testing.T) {
block := common.HexToHash("0x1")
wrongParent := common.HexToHash("0x2")
txByHash := backendmock.WithTransactionByHashFunc(func(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) {
return signedTx, false, nil
})
......@@ -76,19 +80,45 @@ func TestMatchesSender(t *testing.T) {
addr: common.HexToAddress("0xabc"),
}
matcher := transaction.NewMatcher(backendmock.New(txByHash), signer)
trxReceipt := backendmock.WithTransactionReceiptFunc(func(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
return &types.Receipt{
BlockNumber: big.NewInt(0),
BlockHash: block,
}, nil
})
matches, err := matcher.Matches(context.Background(), []byte("0x123"), 0, swarm.NewAddress([]byte{}))
if err != nil {
t.Fatalf("expected no err, got %v", err)
}
headerByNum := backendmock.WithHeaderbyNumberFunc(func(ctx context.Context, number *big.Int) (*types.Header, error) {
return &types.Header{
ParentHash: wrongParent,
}, nil
})
if matches {
t.Fatalf("expected no match, got %v", matches)
matcher := transaction.NewMatcher(backendmock.New(txByHash, trxReceipt, headerByNum), signer)
_, err := matcher.Matches(context.Background(), trx, 0, swarm.NewAddress([]byte{}))
if err == nil {
t.Fatalf("expected no match")
}
})
t.Run("sender matches", func(t *testing.T) {
trxBlock := common.HexToHash("0x2")
nextBlockHeader := &types.Header{
ParentHash: trxBlock,
}
trxReceipt := backendmock.WithTransactionReceiptFunc(func(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
return &types.Receipt{
BlockNumber: big.NewInt(0),
BlockHash: trxBlock,
}, nil
})
headerByNum := backendmock.WithHeaderbyNumberFunc(func(ctx context.Context, number *big.Int) (*types.Header, error) {
return nextBlockHeader, nil
})
txByHash := backendmock.WithTransactionByHashFunc(func(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) {
return signedTx, false, nil
})
......@@ -97,17 +127,13 @@ func TestMatchesSender(t *testing.T) {
addr: common.HexToAddress("0xff"),
}
matcher := transaction.NewMatcher(backendmock.New(txByHash), signer)
matcher := transaction.NewMatcher(backendmock.New(trxReceipt, headerByNum, txByHash), signer)
senderOverlay := crypto.NewOverlayFromEthereumAddress(signer.addr.Bytes(), 0)
senderOverlay := crypto.NewOverlayFromEthereumAddress(signer.addr.Bytes(), 0, nextBlockHeader.Hash().Bytes())
matches, err := matcher.Matches(context.Background(), []byte("0x123"), 0, senderOverlay)
_, err := matcher.Matches(context.Background(), trx, 0, senderOverlay)
if err != nil {
t.Fatalf("expected no err, got %v", err)
}
if !matches {
t.Fatalf("expected match, got %v", matches)
t.Fatalf("expected match")
}
})
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment