-
Notifications
You must be signed in to change notification settings - Fork 110
swap: exchange chain id on handshake #1913
Changes from all commits
eb0ede1
d4a9044
0905b83
87d1f3d
d5c0232
f347be1
e018982
6ae564e
37e5c8e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,68 +44,174 @@ func init() { | |
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true)))) | ||
} | ||
|
||
/* | ||
TestHandshake creates two mock nodes and initiates an exchange; | ||
it expects a handshake to take place between the two nodes | ||
(the handshake would fail because we don't actually use real nodes here) | ||
*/ | ||
func TestHandshake(t *testing.T) { | ||
var err error | ||
// protocol tester based on a swap instance | ||
type swapTester struct { | ||
*p2ptest.ProtocolTester | ||
swap *Swap | ||
} | ||
|
||
// setup test swap object | ||
// creates a new protocol tester for swap with a deployed chequebook | ||
func newSwapTester(t *testing.T) (*swapTester, func(), error) { | ||
swap, clean := newTestSwap(t, ownerKey, nil) | ||
defer clean() | ||
|
||
ctx := context.Background() | ||
err = testDeploy(ctx, swap) | ||
err := testDeploy(context.Background(), swap) | ||
if err != nil { | ||
t.Fatal(err) | ||
return nil, nil, err | ||
} | ||
|
||
// setup the protocolTester, which will allow protocol testing by sending messages | ||
protocolTester := p2ptest.NewProtocolTester(swap.owner.privateKey, 2, swap.run) | ||
protocolTester := p2ptest.NewProtocolTester(swap.owner.privateKey, 1, swap.run) | ||
return &swapTester{ | ||
ProtocolTester: protocolTester, | ||
swap: swap, | ||
}, clean, nil | ||
} | ||
|
||
// creates a test exchange for the handshakes | ||
func HandshakeMsgExchange(lhs, rhs *HandshakeMsg, id enode.ID) []p2ptest.Exchange { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this function exported? |
||
return []p2ptest.Exchange{ | ||
{ | ||
Expects: []p2ptest.Expect{ | ||
{ | ||
Code: 0, | ||
Msg: lhs, | ||
Peer: id, | ||
}, | ||
}, | ||
}, | ||
{ | ||
Triggers: []p2ptest.Trigger{ | ||
{ | ||
Code: 0, | ||
Msg: rhs, | ||
Peer: id, | ||
}, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
// shortcut to creditor node | ||
debitor := protocolTester.Nodes[0] | ||
creditor := protocolTester.Nodes[1] | ||
// helper function for testing the handshake | ||
// lhs is the HandshakeMsg we expect to be sent, rhs the one we receive | ||
// disconnects is a list of disconnect events to be expected | ||
func (s *swapTester) testHandshake(lhs, rhs *HandshakeMsg, disconnects ...*p2ptest.Disconnect) error { | ||
if err := s.TestExchanges(HandshakeMsgExchange(lhs, rhs, s.Nodes[0].ID())...); err != nil { | ||
return err | ||
} | ||
|
||
// set balance artifially | ||
swap.saveBalance(creditor.ID(), -42) | ||
if len(disconnects) > 0 { | ||
return s.TestDisconnected(disconnects...) | ||
} | ||
|
||
// create the expected cheque to be received | ||
cheque := newTestCheque() | ||
// If we don't expect disconnect, ensure peers remain connected | ||
err := s.TestDisconnected(&p2ptest.Disconnect{ | ||
Peer: s.Nodes[0].ID(), | ||
Error: nil, | ||
}) | ||
|
||
// sign the cheque | ||
cheque.Signature, err = cheque.Sign(swap.owner.privateKey) | ||
if err == nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ? Isn't a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, on a disconnect There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a bit counter-intuitive to pass a Then If that's not possible, I guess that's the way to do it and we keep it (I understand you copied it from elsewhere) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Afaik there is no other way to check if the node is disconnected or not. (It also would not be possible, if we passed I would keep the function as it is, so that we remain consistent with the handshake test style from the other protocols. |
||
return fmt.Errorf("Unexpected peer disconnect") | ||
} | ||
|
||
if err.Error() != "timed out waiting for peers to disconnect" { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// creates a new HandshakeMsg | ||
func newSwapHandshakeMsg(contractAddress common.Address, chainID uint64) *HandshakeMsg { | ||
return &HandshakeMsg{ | ||
ContractAddress: contractAddress, | ||
ChainID: chainID, | ||
} | ||
} | ||
|
||
// creates the correct HandshakeMsg based on Swap instance | ||
func correctSwapHandshakeMsg(swap *Swap) *HandshakeMsg { | ||
return newSwapHandshakeMsg(swap.GetParams().ContractAddress, swap.chainID) | ||
} | ||
|
||
// TestHandshake tests the correct handshake scenario | ||
func TestHandshake(t *testing.T) { | ||
// setup the protocolTester, which will allow protocol testing by sending messages | ||
protocolTester, clean, err := newSwapTester(t) | ||
defer clean() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
// run the exchange: | ||
// trigger a `EmitChequeMsg` | ||
// expect HandshakeMsg on each node | ||
err = protocolTester.TestExchanges(p2ptest.Exchange{ | ||
Label: "TestHandshake", | ||
Triggers: []p2ptest.Trigger{ | ||
{ | ||
Code: 0, | ||
Msg: &HandshakeMsg{ | ||
ContractAddress: swap.GetParams().ContractAddress, | ||
}, | ||
Peer: creditor.ID(), | ||
}, | ||
err = protocolTester.testHandshake( | ||
correctSwapHandshakeMsg(protocolTester.swap), | ||
correctSwapHandshakeMsg(protocolTester.swap), | ||
) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
} | ||
|
||
// TestHandshakeInvalidChainID tests that a handshake with the wrong chain id is rejected | ||
func TestHandshakeInvalidChainID(t *testing.T) { | ||
// setup the protocolTester, which will allow protocol testing by sending messages | ||
protocolTester, clean, err := newSwapTester(t) | ||
defer clean() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
err = protocolTester.testHandshake( | ||
correctSwapHandshakeMsg(protocolTester.swap), | ||
newSwapHandshakeMsg(protocolTester.swap.GetParams().ContractAddress, 1234), | ||
&p2ptest.Disconnect{ | ||
Peer: protocolTester.Nodes[0].ID(), | ||
Error: fmt.Errorf("Handshake error: Message handler error: (msg code 0): %v", ErrDifferentChainID), | ||
}, | ||
Expects: []p2ptest.Expect{ | ||
{ | ||
Code: 0, | ||
Msg: &HandshakeMsg{ | ||
ContractAddress: swap.GetParams().ContractAddress, | ||
}, | ||
Peer: debitor.ID(), | ||
}, | ||
) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
} | ||
|
||
// TestHandshakeEmptyContract tests that a handshake with an empty contract address is rejected | ||
func TestHandshakeEmptyContract(t *testing.T) { | ||
// setup the protocolTester, which will allow protocol testing by sending messages | ||
protocolTester, clean, err := newSwapTester(t) | ||
defer clean() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
err = protocolTester.testHandshake( | ||
correctSwapHandshakeMsg(protocolTester.swap), | ||
newSwapHandshakeMsg(common.Address{}, 1234), | ||
&p2ptest.Disconnect{ | ||
Peer: protocolTester.Nodes[0].ID(), | ||
Error: fmt.Errorf("Handshake error: Message handler error: (msg code 0): %v", ErrEmptyAddressInSignature), | ||
}, | ||
}) | ||
) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
} | ||
|
||
// there should be no error at this point | ||
// TestHandshakeInvalidContract tests that a handshake with an address that's not a valid chequebook | ||
func TestHandshakeInvalidContract(t *testing.T) { | ||
// setup the protocolTester, which will allow protocol testing by sending messages | ||
protocolTester, clean, err := newSwapTester(t) | ||
defer clean() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
err = protocolTester.testHandshake( | ||
correctSwapHandshakeMsg(protocolTester.swap), | ||
newSwapHandshakeMsg(ownerAddress, protocolTester.swap.chainID), | ||
&p2ptest.Disconnect{ | ||
Peer: protocolTester.Nodes[0].ID(), | ||
Error: fmt.Errorf("Handshake error: Message handler error: (msg code 0): %v", contract.ErrNotDeployedByFactory), | ||
}, | ||
) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,8 +56,9 @@ type Swap struct { | |
store state.Store // store is needed in order to keep balances and cheques across sessions | ||
peers map[enode.ID]*Peer // map of all swap Peers | ||
peersLock sync.RWMutex // lock for peers map | ||
backend contract.Backend // the backend (blockchain) used | ||
owner *Owner // contract access | ||
backend contract.Backend // the backend (blockchain) used | ||
chainID uint64 // id of the chain the backend is connected to | ||
params *Params // economic and operational parameters | ||
contract contract.Contract // reference to the smart contract | ||
chequebookFactory contract.SimpleSwapFactory // the chequebook factory used | ||
|
@@ -130,7 +131,7 @@ func swapRotatingFileHandler(logdir string) (log.Handler, error) { | |
} | ||
|
||
// newSwapInstance is a swap constructor function without integrity checks | ||
func newSwapInstance(stateStore state.Store, owner *Owner, backend contract.Backend, params *Params, chequebookFactory contract.SimpleSwapFactory) *Swap { | ||
func newSwapInstance(stateStore state.Store, owner *Owner, backend contract.Backend, chainID uint64, params *Params, chequebookFactory contract.SimpleSwapFactory) *Swap { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be great to have the order of function arguments the same as the order of the functions in the swap struct There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This already wasn't the case prior to the PR There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That being said I want the backend and chain id next to each other since they kind of belong together. So it should be changed in the struct. |
||
return &Swap{ | ||
store: stateStore, | ||
peers: make(map[enode.ID]*Peer), | ||
|
@@ -139,6 +140,7 @@ func newSwapInstance(stateStore state.Store, owner *Owner, backend contract.Back | |
params: params, | ||
chequebookFactory: chequebookFactory, | ||
honeyPriceOracle: NewHoneyPriceOracle(), | ||
chainID: chainID, | ||
} | ||
} | ||
|
||
|
@@ -193,6 +195,7 @@ func New(dbPath string, prvkey *ecdsa.PrivateKey, backendURL string, params *Par | |
stateStore, | ||
owner, | ||
backend, | ||
chainID.Uint64(), | ||
params, | ||
factory, | ||
) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you update the comment to the function
verifyHandshake
? Right now it says that it is only verifying the chequebook address transmittedThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done