Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 36 additions & 6 deletions pkg/core/server/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,13 +239,12 @@ func (s *Server) Query(ctx context.Context, req *abcitypes.QueryRequest) (*abcit
}

func (s *Server) CheckTx(_ context.Context, check *abcitypes.CheckTxRequest) (*abcitypes.CheckTxResponse, error) {
// check if protobuf event
_, err := s.isValidSignedTransaction(check.Tx)
if err == nil {
return &abcitypes.CheckTxResponse{Code: abcitypes.CodeTypeOK}, nil
if err != nil {
return &abcitypes.CheckTxResponse{Code: 1}, nil
}

return &abcitypes.CheckTxResponse{Code: 1}, nil
return &abcitypes.CheckTxResponse{Code: abcitypes.CodeTypeOK}, nil
}

func (s *Server) InitChain(_ context.Context, chain *abcitypes.InitChainRequest) (*abcitypes.InitChainResponse, error) {
Expand Down Expand Up @@ -911,10 +910,41 @@ func (s *Server) commitInProgressTx(ctx context.Context) error {

func (s *Server) isValidSignedTransaction(tx []byte) (*v1.SignedTransaction, error) {
var msg v1.SignedTransaction
err := proto.Unmarshal(tx, &msg)
if err != nil {
if err := proto.Unmarshal(tx, &msg); err != nil {
return nil, err
}

if msg.Transaction == nil {
return nil, fmt.Errorf("transaction has no body")
}

switch msg.Transaction.(type) {
case *v1.SignedTransaction_StorageProof:
sp := msg.GetStorageProof()
if len(sp.ProverAddresses) == 0 {
return nil, fmt.Errorf("storage proof has no prover addresses")
}
if sp.Address == "" {
return nil, fmt.Errorf("storage proof has no prover address")
}
if sp.Height == 0 {
return nil, fmt.Errorf("storage proof has no height")
}
case *v1.SignedTransaction_StorageProofVerification:
spv := msg.GetStorageProofVerification()
if spv.Height == 0 {
return nil, fmt.Errorf("storage proof verification has no height")
}
if len(spv.Proof) == 0 {
return nil, fmt.Errorf("storage proof verification has no proof")
}
case *v1.SignedTransaction_Attestation:
att := msg.GetAttestation()
if att.GetValidatorRegistration() == nil && att.GetValidatorDeregistration() == nil {
return nil, fmt.Errorf("attestation has no body")
}
}

return &msg, nil
}

Expand Down
186 changes: 186 additions & 0 deletions pkg/core/server/abci_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
package server

import (
"testing"

v1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
)

func marshalSignedTx(t *testing.T, tx *v1.SignedTransaction) []byte {
t.Helper()
b, err := proto.Marshal(tx)
require.NoError(t, err)
return b
}

func TestIsValidSignedTransaction_EmptyBody(t *testing.T) {
s := &Server{}

t.Run("nil transaction body", func(t *testing.T) {
tx := marshalSignedTx(t, &v1.SignedTransaction{})
_, err := s.isValidSignedTransaction(tx)
require.ErrorContains(t, err, "no body")
})

t.Run("invalid protobuf", func(t *testing.T) {
_, err := s.isValidSignedTransaction([]byte("not protobuf"))
require.Error(t, err)
})
}

func TestIsValidSignedTransaction_StorageProof(t *testing.T) {
s := &Server{}

marshal := func(t *testing.T, sp *v1.StorageProof) []byte {
t.Helper()
return marshalSignedTx(t, &v1.SignedTransaction{
Transaction: &v1.SignedTransaction_StorageProof{StorageProof: sp},
})
}

t.Run("valid", func(t *testing.T) {
tx := marshal(t, &v1.StorageProof{
Height: 100,
Address: "ABCD1234",
ProverAddresses: []string{"ADDR1", "ADDR2"},
Cid: "QmTest",
ProofSignature: []byte("sig"),
})
msg, err := s.isValidSignedTransaction(tx)
require.NoError(t, err)
require.Len(t, msg.GetStorageProof().ProverAddresses, 2)
})

t.Run("empty prover addresses", func(t *testing.T) {
tx := marshal(t, &v1.StorageProof{Height: 100, Address: "ABCD1234"})
_, err := s.isValidSignedTransaction(tx)
require.ErrorContains(t, err, "no prover addresses")
})

t.Run("nil prover addresses after proto roundtrip", func(t *testing.T) {
tx := marshal(t, &v1.StorageProof{Height: 100, Address: "ABCD1234", ProverAddresses: nil})
_, err := s.isValidSignedTransaction(tx)
require.ErrorContains(t, err, "no prover addresses")
})

t.Run("empty slice prover addresses after proto roundtrip", func(t *testing.T) {
tx := marshal(t, &v1.StorageProof{Height: 100, Address: "ABCD1234", ProverAddresses: []string{}})
_, err := s.isValidSignedTransaction(tx)
require.ErrorContains(t, err, "no prover addresses")
})

t.Run("missing address", func(t *testing.T) {
tx := marshal(t, &v1.StorageProof{Height: 100, ProverAddresses: []string{"ADDR1"}})
_, err := s.isValidSignedTransaction(tx)
require.ErrorContains(t, err, "no prover address")
})

t.Run("zero height", func(t *testing.T) {
tx := marshal(t, &v1.StorageProof{Address: "ABCD1234", ProverAddresses: []string{"ADDR1"}})
_, err := s.isValidSignedTransaction(tx)
require.ErrorContains(t, err, "no height")
})
}

func TestIsValidSignedTransaction_StorageProofVerification(t *testing.T) {
s := &Server{}

marshal := func(t *testing.T, spv *v1.StorageProofVerification) []byte {
t.Helper()
return marshalSignedTx(t, &v1.SignedTransaction{
Transaction: &v1.SignedTransaction_StorageProofVerification{StorageProofVerification: spv},
})
}

t.Run("valid", func(t *testing.T) {
tx := marshal(t, &v1.StorageProofVerification{Height: 100, Proof: []byte("proof-data")})
msg, err := s.isValidSignedTransaction(tx)
require.NoError(t, err)
require.Equal(t, int64(100), msg.GetStorageProofVerification().Height)
})

t.Run("zero height", func(t *testing.T) {
tx := marshal(t, &v1.StorageProofVerification{Proof: []byte("proof-data")})
_, err := s.isValidSignedTransaction(tx)
require.ErrorContains(t, err, "no height")
})

t.Run("empty proof", func(t *testing.T) {
tx := marshal(t, &v1.StorageProofVerification{Height: 100})
_, err := s.isValidSignedTransaction(tx)
require.ErrorContains(t, err, "no proof")
})
}

func TestIsValidSignedTransaction_Attestation(t *testing.T) {
s := &Server{}

marshal := func(t *testing.T, att *v1.Attestation) []byte {
t.Helper()
return marshalSignedTx(t, &v1.SignedTransaction{
Transaction: &v1.SignedTransaction_Attestation{Attestation: att},
})
}

t.Run("valid registration", func(t *testing.T) {
tx := marshal(t, &v1.Attestation{
Signatures: []string{"sig1", "sig2"},
Body: &v1.Attestation_ValidatorRegistration{
ValidatorRegistration: &v1.ValidatorRegistration{},
},
})
_, err := s.isValidSignedTransaction(tx)
require.NoError(t, err)
})

t.Run("valid deregistration", func(t *testing.T) {
tx := marshal(t, &v1.Attestation{
Signatures: []string{"sig1"},
Body: &v1.Attestation_ValidatorDeregistration{
ValidatorDeregistration: &v1.ValidatorDeregistration{},
},
})
_, err := s.isValidSignedTransaction(tx)
require.NoError(t, err)
})

t.Run("empty signatures pass structural validation", func(t *testing.T) {
tx := marshal(t, &v1.Attestation{
Body: &v1.Attestation_ValidatorRegistration{
ValidatorRegistration: &v1.ValidatorRegistration{},
},
})
_, err := s.isValidSignedTransaction(tx)
require.NoError(t, err)
})

t.Run("no body", func(t *testing.T) {
tx := marshal(t, &v1.Attestation{
Signatures: []string{"sig1"},
})
_, err := s.isValidSignedTransaction(tx)
require.ErrorContains(t, err, "no body")
})
}

func TestIsValidSignedTransaction_OtherTypes(t *testing.T) {
s := &Server{}

t.Run("plays passes through", func(t *testing.T) {
tx := marshalSignedTx(t, &v1.SignedTransaction{
Transaction: &v1.SignedTransaction_Plays{Plays: &v1.TrackPlays{}},
})
_, err := s.isValidSignedTransaction(tx)
require.NoError(t, err)
})

t.Run("manage entity passes through", func(t *testing.T) {
tx := marshalSignedTx(t, &v1.SignedTransaction{
Transaction: &v1.SignedTransaction_ManageEntity{ManageEntity: &v1.ManageEntityLegacy{}},
})
_, err := s.isValidSignedTransaction(tx)
require.NoError(t, err)
})
}
Loading