Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chain: refactor transaction tests #1748

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
291 changes: 132 additions & 159 deletions chain/transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,158 +26,76 @@ var (
_ chain.Action = (*action2)(nil)
)

type abstractMockAction struct{}

func (*abstractMockAction) ComputeUnits(chain.Rules) uint64 {
panic("unimplemented")
}

func (*abstractMockAction) Execute(_ context.Context, _ chain.Rules, _ state.Mutable, _ int64, _ codec.Address, _ ids.ID) (codec.Typed, error) {
panic("unimplemented")
}

func (*abstractMockAction) StateKeys(_ codec.Address, _ ids.ID) state.Keys {
panic("unimplemented")
}

func (*abstractMockAction) ValidRange(chain.Rules) (start int64, end int64) {
panic("unimplemented")
}

type mockTransferAction struct {
abstractMockAction
To codec.Address `serialize:"true" json:"to"`
Value uint64 `serialize:"true" json:"value"`
Memo []byte `serialize:"true" json:"memo"`
}

func (*mockTransferAction) GetTypeID() uint8 {
return 111
}

type action2 struct {
abstractMockAction
A uint64 `serialize:"true" json:"a"`
B uint64 `serialize:"true" json:"b"`
}

func (*action2) GetTypeID() uint8 {
return 222
}

func unmarshalTransfer(p *codec.Packer) (chain.Action, error) {
var transfer mockTransferAction
err := codec.LinearCodec.UnmarshalFrom(p.Packer, &transfer)
return &transfer, err
}

func unmarshalAction2(p *codec.Packer) (chain.Action, error) {
var action action2
err := codec.LinearCodec.UnmarshalFrom(p.Packer, &action)
return &action, err
}

func TestJSONMarshalUnmarshal(t *testing.T) {
func TestJSONMarshalUnmarshalTransaction(t *testing.T) {
require := require.New(t)
testData := setupTransactionTest(require)
tx := getTransactionData()

txData := chain.TransactionData{
Base: &chain.Base{
Timestamp: 1724315246000,
ChainID: [32]byte{1, 2, 3, 4, 5, 6, 7},
MaxFee: 1234567,
},
Actions: []chain.Action{
&mockTransferAction{
To: codec.Address{1, 2, 3, 4},
Value: 4,
Memo: []byte("hello"),
},
&mockTransferAction{
To: codec.Address{4, 5, 6, 7},
Value: 123,
Memo: []byte("world"),
},
&action2{
A: 2,
B: 4,
},
},
}
priv, err := ed25519.GeneratePrivateKey()
require.NoError(err)
factory := auth.NewED25519Factory(priv)

actionCodec := codec.NewTypeParser[chain.Action]()
authCodec := codec.NewTypeParser[chain.Auth]()

err = actionCodec.Register(&mockTransferAction{}, unmarshalTransfer)
require.NoError(err)
err = actionCodec.Register(&action2{}, unmarshalAction2)
require.NoError(err)
err = authCodec.Register(&auth.ED25519{}, auth.UnmarshalED25519)
require.NoError(err)

signedTx, err := txData.Sign(factory)
signedTx, err := tx.Sign(testData.factory)
require.NoError(err)

b, err := json.Marshal(signedTx)
require.NoError(err)

parser := chaintest.NewParser(nil, actionCodec, authCodec, nil)

parser := chaintest.NewParser(nil, testData.actionCodec, testData.authCodec, nil)
var txFromJSON chain.Transaction
err = txFromJSON.UnmarshalJSON(b, parser)
require.NoError(err)
require.Equal(signedTx.Bytes(), txFromJSON.Bytes())
}

// TestMarshalUnmarshal roughly validates that a transaction packs and unpacks correctly
func TestMarshalUnmarshal(t *testing.T) {
// TestMarshalUnmarshalTransactionData roughly validates that a transaction data packs and unpacks correctly.
func TestMarshalUnmarshalTransactionData(t *testing.T) {
require := require.New(t)

tx := chain.TransactionData{
Base: &chain.Base{
Timestamp: 1724315246000,
ChainID: [32]byte{1, 2, 3, 4, 5, 6, 7},
MaxFee: 1234567,
},
Actions: []chain.Action{
&mockTransferAction{
To: codec.Address{1, 2, 3, 4},
Value: 4,
Memo: []byte("hello"),
},
&mockTransferAction{
To: codec.Address{4, 5, 6, 7},
Value: 123,
Memo: []byte("world"),
},
&action2{
A: 2,
B: 4,
},
},
}

priv, err := ed25519.GeneratePrivateKey()
testData := setupTransactionTest(require)
tx := getTransactionData()
writerPacker := codec.NewWriter(0, consts.NetworkSizeLimit)
err := tx.Marshal(writerPacker)
require.NoError(err)
txDataBytes, err := tx.UnsignedBytes()
require.NoError(err)
require.Equal(writerPacker.Bytes(), txDataBytes)
readerPacker := codec.NewReader(writerPacker.Bytes(), consts.NetworkSizeLimit)
unmarshaledTxData, err := chain.UnmarshalTxData(readerPacker, testData.actionCodec)
require.NoError(err)
factory := auth.NewED25519Factory(priv)
require.Equal(tx, *unmarshaledTxData)
}

actionCodec := codec.NewTypeParser[chain.Action]()
authCodec := codec.NewTypeParser[chain.Auth]()
// TestMarshalUnmarshalTransaction roughly validates that a transaction packs and unpacks correctly.
func TestMarshalUnmarshalTransaction(t *testing.T) {
require := require.New(t)
testData := setupTransactionTest(require)
tx := getTransactionData()
// call UnsignedBytes so that the "unsignedBytes" field would get populated.
originalUnsignedTxBytes, err := tx.UnsignedBytes()
require.NoError(err)

err = authCodec.Register(&auth.ED25519{}, auth.UnmarshalED25519)
signedTx, err := tx.Sign(testData.factory)
require.NoError(err)
writerPacker := codec.NewWriter(0, consts.NetworkSizeLimit)
err = signedTx.Marshal(writerPacker)
require.NoError(err)
err = actionCodec.Register(&mockTransferAction{}, unmarshalTransfer)
require.Equal(signedTx.ID(), utils.ToID(writerPacker.Bytes()))
require.Equal(signedTx.Bytes(), writerPacker.Bytes())
unsignedTxBytes, err := signedTx.UnsignedBytes()
require.NoError(err)
err = actionCodec.Register(&action2{}, unmarshalAction2)
require.Equal(unsignedTxBytes, originalUnsignedTxBytes)
require.Len(unsignedTxBytes, 168)

readerPacker := codec.NewReader(writerPacker.Bytes(), consts.NetworkSizeLimit)
unmarshaledTx, err := chain.UnmarshalTx(readerPacker, testData.actionCodec, testData.authCodec)
require.NoError(err)
require.Equal(writerPacker.Bytes(), unmarshaledTx.Bytes())
}

func TestSignTransaction(t *testing.T) {
require := require.New(t)
testData := setupTransactionTest(require)
tx := getTransactionData()

// call UnsignedBytes so that the "unsignedBytes" field would get populated.
txBeforeSignBytes, err := tx.UnsignedBytes()
require.NoError(err)

signedTx, err := tx.Sign(factory)
signedTx, err := tx.Sign(testData.factory)
require.NoError(err)
unsignedTxAfterSignBytes, err := signedTx.TransactionData.UnsignedBytes()
require.NoError(err)
Expand All @@ -187,24 +105,52 @@ func TestMarshalUnmarshal(t *testing.T) {
for i, action := range signedTx.Actions {
require.Equal(tx.Actions[i], action)
}
writerPacker := codec.NewWriter(0, consts.NetworkSizeLimit)
err = signedTx.Marshal(writerPacker)
}

func TestSignRawActionBytesTx(t *testing.T) {
require := require.New(t)
testData := setupTransactionTest(require)
tx := getTransactionData()

signedTx, err := tx.Sign(testData.factory)
require.NoError(err)
require.Equal(signedTx.ID(), utils.ToID(writerPacker.Bytes()))
require.Equal(signedTx.Bytes(), writerPacker.Bytes())

unsignedTxBytes, err := signedTx.UnsignedBytes()
p := codec.NewWriter(0, consts.NetworkSizeLimit)
require.NoError(signedTx.Actions.MarshalInto(p))
actionsBytes := p.Bytes()
rawSignedTxBytes, err := chain.SignRawActionBytesTx(tx.Base, actionsBytes, testData.factory)
require.NoError(err)
originalUnsignedTxBytes, err := tx.UnsignedBytes()
require.Equal(signedTx.Bytes(), rawSignedTxBytes)
}

type transactionTestData struct {
privateKey ed25519.PrivateKey
factory *auth.ED25519Factory
actionCodec *codec.TypeParser[chain.Action]
authCodec *codec.TypeParser[chain.Auth]
}

func setupTransactionTest(require *require.Assertions) transactionTestData {
var err error
testData := transactionTestData{}
testData.privateKey, err = ed25519.GeneratePrivateKey()
require.NoError(err)
testData.factory = auth.NewED25519Factory(testData.privateKey)

require.Equal(unsignedTxBytes, originalUnsignedTxBytes)
require.Len(unsignedTxBytes, 168)
testData.actionCodec = codec.NewTypeParser[chain.Action]()
testData.authCodec = codec.NewTypeParser[chain.Auth]()
err = testData.authCodec.Register(&auth.ED25519{}, auth.UnmarshalED25519)
require.NoError(err)
err = testData.actionCodec.Register(&mockTransferAction{}, unmarshalTransfer)
require.NoError(err)
err = testData.actionCodec.Register(&action2{}, unmarshalAction2)
require.NoError(err)
return testData
}

func TestSignRawActionBytesTx(t *testing.T) {
require := require.New(t)
tx := chain.TransactionData{
// getTransactionData returns a default TransactionData struct used by the tests.
func getTransactionData() chain.TransactionData {
return chain.TransactionData{
Base: &chain.Base{
Timestamp: 1724315246000,
ChainID: [32]byte{1, 2, 3, 4, 5, 6, 7},
Expand All @@ -227,28 +173,55 @@ func TestSignRawActionBytesTx(t *testing.T) {
},
},
}
}

priv, err := ed25519.GeneratePrivateKey()
require.NoError(err)
factory := auth.NewED25519Factory(priv)
type abstractMockAction struct{}

func (*abstractMockAction) ComputeUnits(chain.Rules) uint64 {
panic("unimplemented")
}

actionCodec := codec.NewTypeParser[chain.Action]()
authCodec := codec.NewTypeParser[chain.Auth]()
func (*abstractMockAction) Execute(_ context.Context, _ chain.Rules, _ state.Mutable, _ int64, _ codec.Address, _ ids.ID) (codec.Typed, error) {
panic("unimplemented")
}

err = authCodec.Register(&auth.ED25519{}, auth.UnmarshalED25519)
require.NoError(err)
err = actionCodec.Register(&mockTransferAction{}, unmarshalTransfer)
require.NoError(err)
err = actionCodec.Register(&action2{}, unmarshalAction2)
require.NoError(err)
func (*abstractMockAction) StateKeys(_ codec.Address, _ ids.ID) state.Keys {
panic("unimplemented")
}

signedTx, err := tx.Sign(factory)
require.NoError(err)
func (*abstractMockAction) ValidRange(chain.Rules) (start int64, end int64) {
panic("unimplemented")
}

p := codec.NewWriter(0, consts.NetworkSizeLimit)
require.NoError(signedTx.Actions.MarshalInto(p))
actionsBytes := p.Bytes()
rawSignedTxBytes, err := chain.SignRawActionBytesTx(tx.Base, actionsBytes, factory)
require.NoError(err)
require.Equal(signedTx.Bytes(), rawSignedTxBytes)
type mockTransferAction struct {
abstractMockAction
To codec.Address `serialize:"true" json:"to"`
Value uint64 `serialize:"true" json:"value"`
Memo []byte `serialize:"true" json:"memo"`
}

func (*mockTransferAction) GetTypeID() uint8 {
return 111
}

type action2 struct {
abstractMockAction
A uint64 `serialize:"true" json:"a"`
B uint64 `serialize:"true" json:"b"`
}

func (*action2) GetTypeID() uint8 {
return 222
}

func unmarshalTransfer(p *codec.Packer) (chain.Action, error) {
var transfer mockTransferAction
err := codec.LinearCodec.UnmarshalFrom(p.Packer, &transfer)
return &transfer, err
}

func unmarshalAction2(p *codec.Packer) (chain.Action, error) {
var action action2
err := codec.LinearCodec.UnmarshalFrom(p.Packer, &action)
return &action, err
}