Skip to content

Commit

Permalink
Cleanup warp related code (ava-labs#995)
Browse files Browse the repository at this point in the history
* Cleanup warp related code

* Revert back to pass rules via pointer in handlePrecompileAccept

* goimports

* Remove unused var

* Update signature aggregator new comment

* Update README so it applies to both Coreth and Subnet-EVM
  • Loading branch information
aaronbuchwald authored Nov 28, 2023
1 parent e23ab05 commit 551d07c
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 137 deletions.
1 change: 0 additions & 1 deletion plugin/evm/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import (
const (
defaultAcceptorQueueLimit = 64 // Provides 2 minutes of buffer (2s block target) for a commit delay
defaultPruningEnabled = true
defaultPruneWarpDB = false
defaultCommitInterval = 4096
defaultTrieCleanCache = 512
defaultTrieDirtyCache = 512
Expand Down
116 changes: 0 additions & 116 deletions plugin/evm/vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3264,119 +3264,3 @@ func TestCrossChainMessagestoVM(t *testing.T) {
require.NoError(err)
require.True(calledSendCrossChainAppResponseFn, "sendCrossChainAppResponseFn was not called")
}

func TestMessageSignatureRequestsToVM(t *testing.T) {
_, vm, _, appSender := GenesisVM(t, true, genesisJSONSubnetEVM, "", "")

defer func() {
err := vm.Shutdown(context.Background())
require.NoError(t, err)
}()

// Generate a new warp unsigned message and add to warp backend
warpMessage, err := avalancheWarp.NewUnsignedMessage(vm.ctx.NetworkID, vm.ctx.ChainID, []byte{1, 2, 3})
require.NoError(t, err)

// Add the known message and get its signature to confirm.
err = vm.warpBackend.AddMessage(warpMessage)
require.NoError(t, err)
signature, err := vm.warpBackend.GetMessageSignature(warpMessage.ID())
require.NoError(t, err)

tests := map[string]struct {
messageID ids.ID
expectedResponse [bls.SignatureLen]byte
}{
"known": {
messageID: warpMessage.ID(),
expectedResponse: signature,
},
"unknown": {
messageID: ids.GenerateTestID(),
expectedResponse: [bls.SignatureLen]byte{},
},
}

for name, test := range tests {
calledSendAppResponseFn := false
appSender.SendAppResponseF = func(ctx context.Context, nodeID ids.NodeID, requestID uint32, responseBytes []byte) error {
calledSendAppResponseFn = true
var response message.SignatureResponse
_, err := message.Codec.Unmarshal(responseBytes, &response)
require.NoError(t, err)
require.Equal(t, test.expectedResponse, response.Signature)

return nil
}
t.Run(name, func(t *testing.T) {
var signatureRequest message.Request = message.MessageSignatureRequest{
MessageID: test.messageID,
}

requestBytes, err := message.Codec.Marshal(message.Version, &signatureRequest)
require.NoError(t, err)

// Send the app request and make sure we called SendAppResponseFn
deadline := time.Now().Add(60 * time.Second)
err = vm.Network.AppRequest(context.Background(), ids.GenerateTestNodeID(), 1, deadline, requestBytes)
require.NoError(t, err)
require.True(t, calledSendAppResponseFn)
})
}
}

func TestBlockSignatureRequestsToVM(t *testing.T) {
_, vm, _, appSender := GenesisVM(t, true, genesisJSONSubnetEVM, "", "")

defer func() {
err := vm.Shutdown(context.Background())
require.NoError(t, err)
}()

lastAcceptedID, err := vm.LastAccepted(context.Background())
require.NoError(t, err)

signature, err := vm.warpBackend.GetBlockSignature(lastAcceptedID)
require.NoError(t, err)

tests := map[string]struct {
blockID ids.ID
expectedResponse [bls.SignatureLen]byte
}{
"known": {
blockID: lastAcceptedID,
expectedResponse: signature,
},
"unknown": {
blockID: ids.GenerateTestID(),
expectedResponse: [bls.SignatureLen]byte{},
},
}

for name, test := range tests {
calledSendAppResponseFn := false
appSender.SendAppResponseF = func(ctx context.Context, nodeID ids.NodeID, requestID uint32, responseBytes []byte) error {
calledSendAppResponseFn = true
var response message.SignatureResponse
_, err := message.Codec.Unmarshal(responseBytes, &response)
require.NoError(t, err)
require.Equal(t, test.expectedResponse, response.Signature)

return nil
}
t.Run(name, func(t *testing.T) {
var signatureRequest message.Request = message.BlockSignatureRequest{
BlockID: test.blockID,
}

requestBytes, err := message.Codec.Marshal(message.Version, &signatureRequest)
require.NoError(t, err)

// Send the app request and make sure we called SendAppResponseFn
deadline := time.Now().Add(60 * time.Second)
err = vm.Network.AppRequest(context.Background(), ids.GenerateTestNodeID(), 1, deadline, requestBytes)
require.NoError(t, err)
require.True(t, calledSendAppResponseFn)
})
}
}
117 changes: 117 additions & 0 deletions plugin/evm/vm_warp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/eth/tracers"
"github.com/ava-labs/subnet-evm/params"
"github.com/ava-labs/subnet-evm/plugin/evm/message"
"github.com/ava-labs/subnet-evm/precompile/contract"
"github.com/ava-labs/subnet-evm/predicate"
subnetEVMUtils "github.com/ava-labs/subnet-evm/utils"
Expand Down Expand Up @@ -583,3 +584,119 @@ func TestReceiveWarpMessage(t *testing.T) {
require.NoError(err)
require.JSONEq(string(txTraceResultBytes), string(blockTxTraceResultBytes))
}

func TestMessageSignatureRequestsToVM(t *testing.T) {
_, vm, _, appSender := GenesisVM(t, true, genesisJSONSubnetEVM, "", "")

defer func() {
err := vm.Shutdown(context.Background())
require.NoError(t, err)
}()

// Generate a new warp unsigned message and add to warp backend
warpMessage, err := avalancheWarp.NewUnsignedMessage(vm.ctx.NetworkID, vm.ctx.ChainID, []byte{1, 2, 3})
require.NoError(t, err)

// Add the known message and get its signature to confirm.
err = vm.warpBackend.AddMessage(warpMessage)
require.NoError(t, err)
signature, err := vm.warpBackend.GetMessageSignature(warpMessage.ID())
require.NoError(t, err)

tests := map[string]struct {
messageID ids.ID
expectedResponse [bls.SignatureLen]byte
}{
"known": {
messageID: warpMessage.ID(),
expectedResponse: signature,
},
"unknown": {
messageID: ids.GenerateTestID(),
expectedResponse: [bls.SignatureLen]byte{},
},
}

for name, test := range tests {
calledSendAppResponseFn := false
appSender.SendAppResponseF = func(ctx context.Context, nodeID ids.NodeID, requestID uint32, responseBytes []byte) error {
calledSendAppResponseFn = true
var response message.SignatureResponse
_, err := message.Codec.Unmarshal(responseBytes, &response)
require.NoError(t, err)
require.Equal(t, test.expectedResponse, response.Signature)

return nil
}
t.Run(name, func(t *testing.T) {
var signatureRequest message.Request = message.MessageSignatureRequest{
MessageID: test.messageID,
}

requestBytes, err := message.Codec.Marshal(message.Version, &signatureRequest)
require.NoError(t, err)

// Send the app request and make sure we called SendAppResponseFn
deadline := time.Now().Add(60 * time.Second)
err = vm.Network.AppRequest(context.Background(), ids.GenerateTestNodeID(), 1, deadline, requestBytes)
require.NoError(t, err)
require.True(t, calledSendAppResponseFn)
})
}
}

func TestBlockSignatureRequestsToVM(t *testing.T) {
_, vm, _, appSender := GenesisVM(t, true, genesisJSONSubnetEVM, "", "")

defer func() {
err := vm.Shutdown(context.Background())
require.NoError(t, err)
}()

lastAcceptedID, err := vm.LastAccepted(context.Background())
require.NoError(t, err)

signature, err := vm.warpBackend.GetBlockSignature(lastAcceptedID)
require.NoError(t, err)

tests := map[string]struct {
blockID ids.ID
expectedResponse [bls.SignatureLen]byte
}{
"known": {
blockID: lastAcceptedID,
expectedResponse: signature,
},
"unknown": {
blockID: ids.GenerateTestID(),
expectedResponse: [bls.SignatureLen]byte{},
},
}

for name, test := range tests {
calledSendAppResponseFn := false
appSender.SendAppResponseF = func(ctx context.Context, nodeID ids.NodeID, requestID uint32, responseBytes []byte) error {
calledSendAppResponseFn = true
var response message.SignatureResponse
_, err := message.Codec.Unmarshal(responseBytes, &response)
require.NoError(t, err)
require.Equal(t, test.expectedResponse, response.Signature)

return nil
}
t.Run(name, func(t *testing.T) {
var signatureRequest message.Request = message.BlockSignatureRequest{
BlockID: test.blockID,
}

requestBytes, err := message.Codec.Marshal(message.Version, &signatureRequest)
require.NoError(t, err)

// Send the app request and make sure we called SendAppResponseFn
deadline := time.Now().Add(60 * time.Second)
err = vm.Network.AppRequest(context.Background(), ids.GenerateTestNodeID(), 1, deadline, requestBytes)
require.NoError(t, err)
require.True(t, calledSendAppResponseFn)
})
}
}
3 changes: 1 addition & 2 deletions warp/aggregator/aggregator.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ type Aggregator struct {
client SignatureGetter
}

// New returns a signature aggregator for the chain with the given [state] on the
// given [subnetID], and where [client] can be used to fetch signatures from validators.
// New returns a signature aggregator that will attempt to aggregate signatures from [validators].
func New(client SignatureGetter, validators []*avalancheWarp.Validator, totalWeight uint64) *Aggregator {
return &Aggregator{
client: client,
Expand Down
8 changes: 0 additions & 8 deletions warp/handlers/signature_request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,9 @@ func TestMessageSignatureHandler(t *testing.T) {
require.EqualValues(t, 1, stats.messageSignatureRequest.Count())
require.EqualValues(t, 1, stats.messageSignatureHit.Count())
require.EqualValues(t, 0, stats.messageSignatureMiss.Count())
require.Greater(t, stats.messageSignatureRequestDuration.Value(), int64(0))
require.EqualValues(t, 0, stats.blockSignatureRequest.Count())
require.EqualValues(t, 0, stats.blockSignatureHit.Count())
require.EqualValues(t, 0, stats.blockSignatureMiss.Count())
require.EqualValues(t, stats.blockSignatureRequestDuration.Value(), int64(0))
},
},
"unknown message": {
Expand All @@ -73,11 +71,9 @@ func TestMessageSignatureHandler(t *testing.T) {
require.EqualValues(t, 1, stats.messageSignatureRequest.Count())
require.EqualValues(t, 0, stats.messageSignatureHit.Count())
require.EqualValues(t, 1, stats.messageSignatureMiss.Count())
require.Greater(t, stats.messageSignatureRequestDuration.Value(), int64(0))
require.EqualValues(t, 0, stats.blockSignatureRequest.Count())
require.EqualValues(t, 0, stats.blockSignatureHit.Count())
require.EqualValues(t, 0, stats.blockSignatureMiss.Count())
require.EqualValues(t, stats.blockSignatureRequestDuration.Value(), int64(0))
},
},
}
Expand Down Expand Up @@ -158,11 +154,9 @@ func TestBlockSignatureHandler(t *testing.T) {
require.EqualValues(t, 0, stats.messageSignatureRequest.Count())
require.EqualValues(t, 0, stats.messageSignatureHit.Count())
require.EqualValues(t, 0, stats.messageSignatureMiss.Count())
require.EqualValues(t, stats.messageSignatureRequestDuration.Value(), int64(0))
require.EqualValues(t, 1, stats.blockSignatureRequest.Count())
require.EqualValues(t, 1, stats.blockSignatureHit.Count())
require.EqualValues(t, 0, stats.blockSignatureMiss.Count())
require.Greater(t, stats.blockSignatureRequestDuration.Value(), int64(0))
},
},
"unknown block": {
Expand All @@ -175,11 +169,9 @@ func TestBlockSignatureHandler(t *testing.T) {
require.EqualValues(t, 0, stats.messageSignatureRequest.Count())
require.EqualValues(t, 0, stats.messageSignatureHit.Count())
require.EqualValues(t, 0, stats.messageSignatureMiss.Count())
require.EqualValues(t, stats.messageSignatureRequestDuration.Value(), int64(0))
require.EqualValues(t, 1, stats.blockSignatureRequest.Count())
require.EqualValues(t, 0, stats.blockSignatureHit.Count())
require.EqualValues(t, 1, stats.blockSignatureMiss.Count())
require.Greater(t, stats.blockSignatureRequestDuration.Value(), int64(0))
},
},
}
Expand Down
15 changes: 5 additions & 10 deletions x/warp/README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
# Avalanche Warp Messaging

> **Warning**
> Avalanche Warp Messaging is currently in experimental mode to be used only on ephemeral test networks.
>
> Breaking changes to Avalanche Warp Messaging integration into Subnet-EVM may still be made.
Avalanche Warp Messaging offers a basic primitive to enable Cross-Subnet communication on the Avalanche Network.

It is intended to allow communication between arbitrary Custom Virtual Machines (including, but not limited to Subnet-EVM).
It is intended to allow communication between arbitrary Custom Virtual Machines (including, but not limited to Subnet-EVM and Coreth).

## How does Avalanche Warp Messaging Work

Expand Down Expand Up @@ -51,7 +46,7 @@ Additionally, the `SourceChainID` is excluded because anyone parsing the chain c
- `sender`
- The `messageID` of the unsigned message (sha256 of the unsigned message)

The actual `message` is the entire [Avalanche Warp Unsigned Message](https://github.com/ava-labs/avalanchego/blob/master/vms/platformvm/warp/unsigned_message.go#L14) including the Subnet-EVM [Addressed Payload](../../../warp/payload/payload.go). This is emitted as the unindexed data in the log.
The actual `message` is the entire [Avalanche Warp Unsigned Message](https://github.com/ava-labs/avalanchego/blob/master/vms/platformvm/warp/unsigned_message.go#L14) including an [AddressedCall](https://github.com/ava-labs/avalanchego/tree/v1.10.15/vms/platformvm/warp/payload). The unsigned message is emitted as the unindexed data in the log.

#### getVerifiedMessage

Expand All @@ -70,7 +65,7 @@ This pre-verification is performed using the ProposerVM Block header during [blo

#### getBlockchainID

`getBlockchainID` returns the blockchainID of the blockchain that Subnet-EVM is running on.
`getBlockchainID` returns the blockchainID of the blockchain that the VM is running on.

This is different from the conventional Ethereum ChainID registered to [ChainList](https://chainlist.org/).

Expand Down Expand Up @@ -120,7 +115,7 @@ This means verifying the Warp Message and therefore the state transition on a bl

The Avalanche P-Chain tracks only its current state and reverse diff layers (reversing the changes from past blocks) in order to re-calculate the validator set at a historical height. This means calculating a very old validator set that is used to verify a Warp Message in an old block may become prohibitively expensive.

Therefore, we need a heuristic to ensure that the network can correctly re-process old blocks (note: re-processing old blocks is a requirement to perform bootstrapping and is used in some VMs including Subnet-EVM to serve or verify historical data).
Therefore, we need a heuristic to ensure that the network can correctly re-process old blocks (note: re-processing old blocks is a requirement to perform bootstrapping and is used in some VMs to serve or verify historical data).

As a result, we require that the block itself provides a deterministic hint which determines which Avalanche Warp Messages were considered valid/invalid during the block's execution. This ensures that we can always re-process blocks and use the hint to decide whether an Avalanche Warp Message should be treated as valid/invalid even after the P-Chain state that was used at the original execution time may no longer support fast lookups.

Expand All @@ -143,7 +138,7 @@ In contrast, Avalanche Warp Messages are validated within the context of an exac

#### Guarantees Offered by Warp Precompile

The Warp Precompile was designed with the intention of minimizing the trusted computing base for Subnet-EVM. Therefore, it makes several tradeoffs which encourage users to use protocols built ON TOP of the Warp Precompile itself as opposed to directly using the Warp Precompile.
The Warp Precompile was designed with the intention of minimizing the trusted computing base for the VM as much as possible. Therefore, it makes several tradeoffs which encourage users to use protocols built ON TOP of the Warp Precompile itself as opposed to directly using the Warp Precompile.

The Warp Precompile itself provides ONLY the following ability:

Expand Down

0 comments on commit 551d07c

Please sign in to comment.