github.com/mavryk-network/mvgo@v1.19.9/micheline/marshal_test.go (about)

     1  // Copyright (c) 2023 Blockwatch Data Inc.
     2  // Author: alex@blockwatch.cc
     3  //
     4  
     5  package micheline
     6  
     7  import (
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/mavryk-network/mvgo/mavryk"
    12  )
    13  
    14  type marshalTest struct {
    15  	Name      string
    16  	Spec      string
    17  	Value     any
    18  	Optimized bool
    19  	Want      string
    20  }
    21  
    22  var marshalTests = []marshalTest{
    23  	// scalars
    24  	// int
    25  	{
    26  		Name:      "int",
    27  		Spec:      `{"annots": ["%payoutDelay"],"prim": "int"}`,
    28  		Value:     map[string]any{"payoutDelay": 1},
    29  		Optimized: false,
    30  		Want:      `{"int":"1"}`,
    31  	},
    32  	//   nat
    33  	{
    34  		Name:      "nat",
    35  		Spec:      `{"annots": ["%payoutFrequency"],"prim": "nat"}`,
    36  		Value:     map[string]any{"payoutFrequency": 1},
    37  		Optimized: false,
    38  		Want:      `{"int":"1"}`,
    39  	},
    40  	//   mumav
    41  	{
    42  		Name:      "mumav",
    43  		Spec:      `{"annots": ["%money"],"prim": "mumav"}`,
    44  		Value:     map[string]any{"money": 1},
    45  		Optimized: false,
    46  		Want:      `{"int":"1"}`,
    47  	},
    48  	//   string
    49  	{
    50  		Name:      "string",
    51  		Spec:      `{"annots": ["%name"],"prim": "string"}`,
    52  		Value:     map[string]any{"name": "hello"},
    53  		Optimized: false,
    54  		Want:      `{"string":"hello"}`,
    55  	},
    56  	//   bytes
    57  	{
    58  		Name:      "bytes",
    59  		Spec:      `{"annots": ["%bakerName"],"prim": "bytes"}`,
    60  		Value:     map[string]any{"bakerName": []byte{0xfa, 0xfe}},
    61  		Optimized: false,
    62  		Want:      `{"bytes":"fafe"}`,
    63  	},
    64  	//   bool
    65  	{
    66  		Name:      "bool",
    67  		Spec:      `{"annots": ["%bakerChargesTransactionFee"],"prim": "bool"}`,
    68  		Value:     map[string]any{"bakerChargesTransactionFee": true},
    69  		Optimized: false,
    70  		Want:      `{"prim":"True"}`,
    71  	},
    72  	//   timestamp
    73  	{
    74  		Name:      "timestamp_unix_noopt",
    75  		Spec:      `{"annots": ["%last_update"],"prim": "timestamp"}`,
    76  		Value:     map[string]any{"last_update": time.Unix(1696945397, 0)},
    77  		Optimized: false,
    78  		Want:      `{"string":"2023-10-10T13:43:17Z"}`,
    79  	},
    80  	{
    81  		Name:      "timestamp_unix_opt",
    82  		Spec:      `{"annots": ["%last_update"],"prim": "timestamp"}`,
    83  		Value:     map[string]any{"last_update": time.Unix(1696945397, 0)},
    84  		Optimized: true,
    85  		Want:      `{"int":"1696945397"}`,
    86  	},
    87  	{
    88  		Name:      "timestamp_rfc_noopt",
    89  		Spec:      `{"annots": ["%last_update"],"prim": "timestamp"}`,
    90  		Value:     map[string]any{"last_update": "2023-10-10T13:43:17Z"},
    91  		Optimized: false,
    92  		Want:      `{"string":"2023-10-10T13:43:17Z"}`,
    93  	},
    94  	{
    95  		Name:      "timestamp_rfc_opt",
    96  		Spec:      `{"annots": ["%last_update"],"prim": "timestamp"}`,
    97  		Value:     map[string]any{"last_update": "2023-10-10T13:43:17Z"},
    98  		Optimized: true,
    99  		Want:      `{"int":"1696945397"}`,
   100  	},
   101  	//   key_hash
   102  	{
   103  		Name:      "key_hash_string_opt",
   104  		Spec:      `{"annots": ["%baker"],"prim": "key_hash"}`,
   105  		Value:     map[string]any{"baker": "mv1NUuSgQ3rvNBZE9FWTdgQ2oEomsRvP1Le4"},
   106  		Optimized: false,
   107  		Want:      `{"string":"mv1NUuSgQ3rvNBZE9FWTdgQ2oEomsRvP1Le4"}`,
   108  	},
   109  	{
   110  		Name:      "key_hash_string_noopt",
   111  		Spec:      `{"annots": ["%baker"],"prim": "key_hash"}`,
   112  		Value:     map[string]any{"baker": "mv1NUuSgQ3rvNBZE9FWTdgQ2oEomsRvP1Le4"},
   113  		Optimized: true,
   114  		Want:      `{"bytes":"009ec090ac91d1351dc4791bdb48a98f64421caa1c"}`,
   115  	},
   116  	{
   117  		Name:      "key_hash_addr_opt",
   118  		Spec:      `{"annots": ["%baker"],"prim": "key_hash"}`,
   119  		Value:     map[string]any{"baker": mavryk.MustParseAddress("mv1NUuSgQ3rvNBZE9FWTdgQ2oEomsRvP1Le4")},
   120  		Optimized: false,
   121  		Want:      `{"string":"mv1NUuSgQ3rvNBZE9FWTdgQ2oEomsRvP1Le4"}`,
   122  	},
   123  	{
   124  		Name:      "key_hash_addr_noopt",
   125  		Spec:      `{"annots": ["%baker"],"prim": "key_hash"}`,
   126  		Value:     map[string]any{"baker": mavryk.MustParseAddress("mv1NUuSgQ3rvNBZE9FWTdgQ2oEomsRvP1Le4")},
   127  		Optimized: true,
   128  		Want:      `{"bytes":"009ec090ac91d1351dc4791bdb48a98f64421caa1c"}`,
   129  	},
   130  	//   address
   131  	{
   132  		Name:      "address_string_opt",
   133  		Spec:      `{"annots": ["%reporterAccount"],"prim": "address"}`,
   134  		Value:     map[string]any{"reporterAccount": "mv1NUuSgQ3rvNBZE9FWTdgQ2oEomsRvP1Le4"},
   135  		Optimized: false,
   136  		Want:      `{"string":"mv1NUuSgQ3rvNBZE9FWTdgQ2oEomsRvP1Le4"}`,
   137  	},
   138  	{
   139  		Name:      "address_string_noopt",
   140  		Spec:      `{"annots": ["%reporterAccount"],"prim": "address"}`,
   141  		Value:     map[string]any{"reporterAccount": "mv1NUuSgQ3rvNBZE9FWTdgQ2oEomsRvP1Le4"},
   142  		Optimized: true,
   143  		Want:      `{"bytes":"00009ec090ac91d1351dc4791bdb48a98f64421caa1c"}`,
   144  	},
   145  	{
   146  		Name:      "address_addr_opt",
   147  		Spec:      `{"annots": ["%reporterAccount"],"prim": "address"}`,
   148  		Value:     map[string]any{"reporterAccount": mavryk.MustParseAddress("mv1NUuSgQ3rvNBZE9FWTdgQ2oEomsRvP1Le4")},
   149  		Optimized: false,
   150  		Want:      `{"string":"mv1NUuSgQ3rvNBZE9FWTdgQ2oEomsRvP1Le4"}`,
   151  	},
   152  	{
   153  		Name:      "address_addr_noopt",
   154  		Spec:      `{"annots": ["%reporterAccount"],"prim": "address"}`,
   155  		Value:     map[string]any{"reporterAccount": mavryk.MustParseAddress("mv1NUuSgQ3rvNBZE9FWTdgQ2oEomsRvP1Le4")},
   156  		Optimized: true,
   157  		Want:      `{"bytes":"00009ec090ac91d1351dc4791bdb48a98f64421caa1c"}`,
   158  	},
   159  	//   key
   160  	{
   161  		Name:      "key_string_noopt",
   162  		Spec:      `{"annots": ["%pour_authorizer"],"prim": "key"}`,
   163  		Value:     map[string]any{"pour_authorizer": "edpkvEfPbB2Q8dpo8D7DcLXC7ft4ogfeayPzxnvTiuz3iKM9TonxHh"},
   164  		Optimized: false,
   165  		Want:      `{"string":"edpkvEfPbB2Q8dpo8D7DcLXC7ft4ogfeayPzxnvTiuz3iKM9TonxHh"}`,
   166  	},
   167  	{
   168  		Name:      "key_string_opt",
   169  		Spec:      `{"annots": ["%pour_authorizer"],"prim": "key"}`,
   170  		Value:     map[string]any{"pour_authorizer": "edpkvEfPbB2Q8dpo8D7DcLXC7ft4ogfeayPzxnvTiuz3iKM9TonxHh"},
   171  		Optimized: true,
   172  		Want:      `{"bytes":"00d1e4cae729906793005dbdfddef090c4153042bf922e4db3a99a0467c45b9898"}`,
   173  	},
   174  	{
   175  		Name:      "key_key_noopt",
   176  		Spec:      `{"annots": ["%pour_authorizer"],"prim": "key"}`,
   177  		Value:     map[string]any{"pour_authorizer": mavryk.MustParseKey("edpkvEfPbB2Q8dpo8D7DcLXC7ft4ogfeayPzxnvTiuz3iKM9TonxHh")},
   178  		Optimized: false,
   179  		Want:      `{"string":"edpkvEfPbB2Q8dpo8D7DcLXC7ft4ogfeayPzxnvTiuz3iKM9TonxHh"}`,
   180  	},
   181  	{
   182  		Name:      "key_key_opt",
   183  		Spec:      `{"annots": ["%pour_authorizer"],"prim": "key"}`,
   184  		Value:     map[string]any{"pour_authorizer": mavryk.MustParseKey("edpkvEfPbB2Q8dpo8D7DcLXC7ft4ogfeayPzxnvTiuz3iKM9TonxHh")},
   185  		Optimized: true,
   186  		Want:      `{"bytes":"00d1e4cae729906793005dbdfddef090c4153042bf922e4db3a99a0467c45b9898"}`,
   187  	},
   188  	//   unit
   189  	{
   190  		Name:      "unit_string",
   191  		Spec:      `{"annots": ["%arg"],"prim": "unit"}`,
   192  		Value:     map[string]any{"arg": ""},
   193  		Optimized: false,
   194  		Want:      `{"prim":"Unit"}`,
   195  	},
   196  	{
   197  		Name:      "unit_nil",
   198  		Spec:      `{"annots": ["%arg"],"prim": "unit"}`,
   199  		Value:     map[string]any{"arg": nil},
   200  		Optimized: false,
   201  		Want:      `{"prim":"Unit"}`,
   202  	},
   203  	//   signature
   204  	{
   205  		Name:      "signature_string_noopt",
   206  		Spec:      `{"annots": ["%sig"],"prim": "signature"}`,
   207  		Value:     map[string]any{"sig": "sigqgQgW5qQCsuHP5HhMhAYR2HjcChUE7zAczsyCdF681rfZXpxnXFHu3E6ycmz4pQahjvu3VLfa7FMCxZXmiMiuZFQS4MHy"},
   208  		Optimized: false,
   209  		Want:      `{"string":"sigqgQgW5qQCsuHP5HhMhAYR2HjcChUE7zAczsyCdF681rfZXpxnXFHu3E6ycmz4pQahjvu3VLfa7FMCxZXmiMiuZFQS4MHy"}`,
   210  	},
   211  	{
   212  		Name:      "signature_string_opt",
   213  		Spec:      `{"annots": ["%sig"],"prim": "signature"}`,
   214  		Value:     map[string]any{"sig": "sigqgQgW5qQCsuHP5HhMhAYR2HjcChUE7zAczsyCdF681rfZXpxnXFHu3E6ycmz4pQahjvu3VLfa7FMCxZXmiMiuZFQS4MHy"},
   215  		Optimized: true,
   216  		Want:      `{"bytes":"d3a9e1467b32104921d4e2dd93265739c1a5faee7a7f8880842b096c0b6714200c43fd5872f82581dfe1cb3a76ccdadaa4d6361d72b4abee6884cb7ed87f0b04"}`,
   217  	},
   218  	{
   219  		Name:      "signature_sig_noopt",
   220  		Spec:      `{"annots": ["%sig"],"prim": "signature"}`,
   221  		Value:     map[string]any{"sig": mavryk.MustParseSignature("sigqgQgW5qQCsuHP5HhMhAYR2HjcChUE7zAczsyCdF681rfZXpxnXFHu3E6ycmz4pQahjvu3VLfa7FMCxZXmiMiuZFQS4MHy")},
   222  		Optimized: false,
   223  		Want:      `{"string":"sigqgQgW5qQCsuHP5HhMhAYR2HjcChUE7zAczsyCdF681rfZXpxnXFHu3E6ycmz4pQahjvu3VLfa7FMCxZXmiMiuZFQS4MHy"}`,
   224  	},
   225  	{
   226  		Name:      "signature_sig_opt",
   227  		Spec:      `{"annots": ["%sig"],"prim": "signature"}`,
   228  		Value:     map[string]any{"sig": mavryk.MustParseSignature("sigqgQgW5qQCsuHP5HhMhAYR2HjcChUE7zAczsyCdF681rfZXpxnXFHu3E6ycmz4pQahjvu3VLfa7FMCxZXmiMiuZFQS4MHy")},
   229  		Optimized: true,
   230  		Want:      `{"bytes":"d3a9e1467b32104921d4e2dd93265739c1a5faee7a7f8880842b096c0b6714200c43fd5872f82581dfe1cb3a76ccdadaa4d6361d72b4abee6884cb7ed87f0b04"}`,
   231  	},
   232  	//   arg list
   233  	{
   234  		Name:      "args_named",
   235  		Spec:      `{"args":[{"prim":"nat","annots":["%num1"]},{"args":[{"prim":"string","annots":["%name"]},{"prim":"int","annots":["%num2"]}],"prim": "pair"}],"prim": "pair"}`,
   236  		Value:     map[string]any{"num1": 1, "name": "hello", "num2": 42},
   237  		Optimized: false,
   238  		Want:      `{"prim":"Pair","args":[{"int":"1"},{"prim":"Pair","args":[{"string":"hello"},{"int":"42"}]}]}`,
   239  	},
   240  	//   anon arg list
   241  	{
   242  		Name:      "args_anon",
   243  		Spec:      `{"args":[{"prim":"nat"},{"args":[{"prim":"string"},{"prim":"int"}],"prim": "pair"}],"prim": "pair"}`,
   244  		Value:     map[string]any{"0": 1, "1": "hello", "2": 42},
   245  		Optimized: false,
   246  		Want:      `{"prim":"Pair","args":[{"int":"1"},{"prim":"Pair","args":[{"string":"hello"},{"int":"42"}]}]}`,
   247  	},
   248  	// set
   249  	{
   250  		Name:      "set",
   251  		Spec:      `{"annots": ["%admins"],"prim": "set", "args": [{"prim": "key_hash"}]}`,
   252  		Value:     map[string]any{"admins": []any{"mv1NUuSgQ3rvNBZE9FWTdgQ2oEomsRvP1Le4"}},
   253  		Optimized: false,
   254  		Want:      `[{"string":"mv1NUuSgQ3rvNBZE9FWTdgQ2oEomsRvP1Le4"}]`,
   255  	},
   256  	{
   257  		Name:      "set",
   258  		Spec:      `{"annots": ["%admins"],"prim": "set", "args": [{"prim": "key_hash"}]}`,
   259  		Value:     map[string]any{"admins": []any{mavryk.MustParseAddress("mv1NUuSgQ3rvNBZE9FWTdgQ2oEomsRvP1Le4")}},
   260  		Optimized: false,
   261  		Want:      `[{"string":"mv1NUuSgQ3rvNBZE9FWTdgQ2oEomsRvP1Le4"}]`,
   262  	},
   263  	// map
   264  	{
   265  		Name:      "map",
   266  		Spec:      `{"annots":["%approvals"],"prim":"map","args":[{"prim":"address"},{"prim":"nat"}]}`,
   267  		Value:     map[string]any{"approvals": map[string]any{"mv1NUuSgQ3rvNBZE9FWTdgQ2oEomsRvP1Le4": "1"}},
   268  		Optimized: false,
   269  		Want:      `[{"prim":"Elt","args":[{"string":"mv1NUuSgQ3rvNBZE9FWTdgQ2oEomsRvP1Le4"},{"int":"1"}]}]`,
   270  	},
   271  	{
   272  		Name:      "map",
   273  		Spec:      `{"prim":"pair","annots":["%add_token"],"args":[{"prim":"nat","annots":["%token_id"]},{"prim":"map","annots":["%token_info"],"args":[{"prim":"string"},{"prim":"bytes"}]}]}`,
   274  		Value:     map[string]any{"token_id": "1", "token_info": map[string]any{"": "ipfs://Qmb94zFKazBKxuYk4QyTWmgiVP3zXLkGjNDDTq3DShEs8E"}},
   275  		Optimized: false,
   276  		Want:      `{"prim":"Pair","args":[{"int":"1"},[{"prim":"Elt","args":[{"string":""},{"bytes":"697066733a2f2f516d6239347a464b617a424b7875596b34517954576d67695650337a584c6b476a4e444454713344536845733845"}]}]]}`,
   277  	},
   278  
   279  	// option
   280  	{
   281  		Name:      "option_no_value",
   282  		Spec:      `{"annots":["%reporterAccount"],"prim":"option","args":[{"prim":"address"}]}`,
   283  		Value:     map[string]any{"reporterAccount": nil},
   284  		Optimized: false,
   285  		Want:      `{"prim":"None"}`,
   286  	},
   287  	{
   288  		Name:      "option_with_value",
   289  		Spec:      `{"annots":["%reporterAccount"],"prim":"option","args":[{"prim":"address"}]}`,
   290  		Value:     map[string]any{"reporterAccount": "mv1NUuSgQ3rvNBZE9FWTdgQ2oEomsRvP1Le4"},
   291  		Optimized: false,
   292  		Want:      `{"prim":"Some","args":[{"string":"mv1NUuSgQ3rvNBZE9FWTdgQ2oEomsRvP1Le4"}]}`,
   293  	},
   294  
   295  	// named union type
   296  	{
   297  		Name:      "named-union",
   298  		Spec:      `{"annots":["%update_operators"],"args":[{"args":[{"annots":["%add_operator"],"args":[{"annots":["%owner"],"prim":"address"},{"args":[{"annots":["%operator"],"prim":"address"},{"annots":["%token_id"],"prim":"nat"}],"prim":"pair"}],"prim":"pair"},{"annots":["%remove_operator"],"args":[{"annots":["%owner"],"prim":"address"},{"args":[{"annots":["%operator"],"prim":"address"},{"annots":["%token_id"],"prim":"nat"}],"prim":"pair"}],"prim":"pair"}],"prim":"or"}],"prim":"list"}`,
   299  		Value:     map[string]any{"update_operators": []any{map[string]any{"add_operator": map[string]any{"owner": "mv1NUuSgQ3rvNBZE9FWTdgQ2oEomsRvP1Le4", "operator": "mv1NUuSgQ3rvNBZE9FWTdgQ2oEomsRvP1Le4", "token_id": "0"}}}},
   300  		Optimized: false,
   301  		Want:      `[{"prim":"Left","args":[{"prim":"Pair","args":[{"string":"mv1NUuSgQ3rvNBZE9FWTdgQ2oEomsRvP1Le4"},{"prim":"Pair","args":[{"string":"mv1NUuSgQ3rvNBZE9FWTdgQ2oEomsRvP1Le4"},{"int":"0"}]}]}]}]`,
   302  	},
   303  
   304  	// TODO
   305  
   306  	// // anonymous union type
   307  	// {
   308  	// 	Name: "anon-union",
   309  	// 	Spec: `{"args":[{"args":[{"prim":"unit"},{"prim":"operation"}],"prim":"lambda"},{"args":[{"prim":"key_hash"}],"prim":"set"}],"prim":"or"}`,
   310  	// },
   311  	// // nested map
   312  	// {
   313  	// 	Name: "nested_map",
   314  	// 	Spec: `{"annots": ["%deck"],"args": [{"prim": "int"},{"args": [{"prim": "int"},{"prim": "int"}],"prim": "map"}],"prim": "map"}`,
   315  	// },
   316  	// // nested list (FA2)
   317  	// {
   318  	// 	Name: "nested_list",
   319  	// 	Spec: `{"annots": ["%transfer"],"args": [{"args": [{"annots": ["%from_"],"prim": "address"},{"annots": ["%txs"],"args": [{"args": [{"annots": ["%to_"],"prim": "address"},{"args": [{"annots": ["%token_id"],"prim": "nat"},{"annots": ["%amount"],"prim": "nat"}],"prim": "pair"}],"prim": "pair"}],"prim": "list"}],"prim": "pair"}],"prim": "list"}`,
   320  	// },
   321  	// // right-hand pair tree
   322  	// {
   323  	// 	Name: "right_hand_pair_tree",
   324  	// 	Spec: `{"args":[{"annots":["%tokenPool"],"prim":"nat"},{"args":[{"annots":["%xtzPool"],"prim":"mumav"},{"args":[{"annots":["%lqtTotal"],"prim":"nat"},{"args":[{"annots":["%tokenAddress"],"prim":"address"},{"annots":["%lqtAddress"],"prim":"address"}],"prim":"pair"}],"prim":"pair"}],"prim":"pair"}],"prim":"pair"}`,
   325  	// },
   326  
   327  	// unsupported
   328  	// //   chain_id
   329  	// //   bls12_381_g1
   330  	// //   bls12_381_g2
   331  	// //   bls12_381_fr
   332  	// {
   333  	// 	Name: "bls",
   334  	// 	Spec: `{"annots":["%g2"],"prim":"bls12_381_g2"}`,
   335  	// },
   336  	// //   sapling_state
   337  	// {
   338  	// 	Name: "sapling_state",
   339  	// 	Spec: `{"prim":"sapling_state","args":[{"int":"8"}]}`,
   340  	// },
   341  	// //   sapling_transaction
   342  	// {
   343  	// 	Name: "sapling_transaction",
   344  	// 	Spec: `{"prim":"sapling_transaction","args":[{"int":"8"}]}`,
   345  	// },
   346  	// //   never
   347  	// {
   348  	// 	Name: "never",
   349  	// 	Spec: `{"prim":"never"}`,
   350  	// },
   351  	// // lambda, ticket, callbacks
   352  	// {
   353  	//     Name: "lambda",
   354  	//     Spec: `{"args": [{"args": [{"args": [{"prim": "string"},{"prim": "bytes"}],"prim": "pair"},{"args": [{"prim": "bytes"},{"prim": "bytes"}],"prim": "big_map"}],"prim": "pair"},{"args": [{"args": [{"prim": "operation"}],"prim": "list"},{"args": [{"prim": "bytes"},{"prim": "bytes"}],"prim": "big_map"}],"prim": "pair"}],"prim": "lambda"}`,
   355  	// },
   356  	// // ticket
   357  	// {
   358  	//     Name: "ticket",
   359  	//     Spec: `{"prim": "ticket", "args":[{"prim":"timestamp"}]}`,
   360  	// },
   361  	// // ticket 2
   362  	// {
   363  	//     Name: "ticket2",
   364  	//     Spec: `{"prim": "ticket", "annots":["%save"], "args":[{"prim":"string"}]}`,
   365  	// },
   366  	// // contract
   367  	// {
   368  	//     Name: "contract",
   369  	//     Spec: `{"annots": ["%pour_dest"],"args": [{"prim": "unit"}],"prim": "contract"}`,
   370  	// },
   371  }
   372  
   373  func TestTypeMarshaling(t *testing.T) {
   374  	for _, test := range marshalTests {
   375  		t.Run(test.Name, func(T *testing.T) {
   376  			typ := Type{}
   377  			err := typ.UnmarshalJSON([]byte(test.Spec))
   378  			if err != nil {
   379  				T.Fatalf("unmarshal error: %v", err)
   380  			}
   381  			prim, err := typ.Typedef("").Marshal(test.Value, test.Optimized)
   382  			if err != nil {
   383  				T.Fatalf("marshal error: %v", err)
   384  			}
   385  			have, err := prim.MarshalJSON()
   386  			if err != nil {
   387  				T.Fatalf("render error: %v", err)
   388  			}
   389  			// fmt.Printf("HAVE: %s\n", string(have))
   390  			if !jsonDiff(T, have, []byte(test.Want)) {
   391  				T.Error("render mismatch, see log for details")
   392  				t.FailNow()
   393  			}
   394  		})
   395  	}
   396  }