github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/wasm/types/proposal_test.go (about) 1 package types 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "strings" 7 "testing" 8 9 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 10 govtypes "github.com/fibonacci-chain/fbc/x/gov/types" 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 "gopkg.in/yaml.v2" 14 ) 15 16 func TestValidateProposalCommons(t *testing.T) { 17 type commonProposal struct { 18 Title, Description string 19 } 20 21 specs := map[string]struct { 22 src commonProposal 23 expErr bool 24 }{ 25 "all good": {src: commonProposal{ 26 Title: "Foo", 27 Description: "Bar", 28 }}, 29 "prevent empty title": { 30 src: commonProposal{ 31 Description: "Bar", 32 }, 33 expErr: true, 34 }, 35 "prevent white space only title": { 36 src: commonProposal{ 37 Title: " ", 38 Description: "Bar", 39 }, 40 expErr: true, 41 }, 42 "prevent leading white spaces in title": { 43 src: commonProposal{ 44 Title: " Foo", 45 Description: "Bar", 46 }, 47 expErr: true, 48 }, 49 "prevent title exceeds max length ": { 50 src: commonProposal{ 51 Title: strings.Repeat("a", govtypes.MaxTitleLength+1), 52 Description: "Bar", 53 }, 54 expErr: true, 55 }, 56 "prevent empty description": { 57 src: commonProposal{ 58 Title: "Foo", 59 }, 60 expErr: true, 61 }, 62 "prevent leading white spaces in description": { 63 src: commonProposal{ 64 Title: "Foo", 65 Description: " Bar", 66 }, 67 expErr: true, 68 }, 69 "prevent white space only description": { 70 src: commonProposal{ 71 Title: "Foo", 72 Description: " ", 73 }, 74 expErr: true, 75 }, 76 "prevent descr exceeds max length ": { 77 src: commonProposal{ 78 Title: "Foo", 79 Description: strings.Repeat("a", govtypes.MaxDescriptionLength+1), 80 }, 81 expErr: true, 82 }, 83 } 84 for msg, spec := range specs { 85 t.Run(msg, func(t *testing.T) { 86 err := validateProposalCommons(spec.src.Title, spec.src.Description) 87 if spec.expErr { 88 require.Error(t, err) 89 } else { 90 require.NoError(t, err) 91 } 92 }) 93 } 94 } 95 96 func TestValidateStoreCodeProposal(t *testing.T) { 97 var ( 98 anyAddress sdk.AccAddress = bytes.Repeat([]byte{0x0}, ContractAddrLen) 99 invalidAddress = "invalid address" 100 ) 101 102 specs := map[string]struct { 103 src *StoreCodeProposal 104 expErr bool 105 }{ 106 "all good": { 107 src: StoreCodeProposalFixture(), 108 }, 109 "with instantiate permission": { 110 src: StoreCodeProposalFixture(func(p *StoreCodeProposal) { 111 accessConfig := AccessTypeOnlyAddress.With(anyAddress) 112 p.InstantiatePermission = &accessConfig 113 }), 114 }, 115 "base data missing": { 116 src: StoreCodeProposalFixture(func(p *StoreCodeProposal) { 117 p.Title = "" 118 }), 119 expErr: true, 120 }, 121 "run_as missing": { 122 src: StoreCodeProposalFixture(func(p *StoreCodeProposal) { 123 p.RunAs = "" 124 }), 125 expErr: true, 126 }, 127 "run_as invalid": { 128 src: StoreCodeProposalFixture(func(p *StoreCodeProposal) { 129 p.RunAs = invalidAddress 130 }), 131 expErr: true, 132 }, 133 "wasm code missing": { 134 src: StoreCodeProposalFixture(func(p *StoreCodeProposal) { 135 p.WASMByteCode = nil 136 }), 137 expErr: true, 138 }, 139 "wasm code invalid": { 140 src: StoreCodeProposalFixture(func(p *StoreCodeProposal) { 141 p.WASMByteCode = bytes.Repeat([]byte{0x0}, MaxWasmSize+1) 142 }), 143 expErr: true, 144 }, 145 "with invalid instantiate permission": { 146 src: StoreCodeProposalFixture(func(p *StoreCodeProposal) { 147 p.InstantiatePermission = &AccessConfig{} 148 }), 149 expErr: true, 150 }, 151 } 152 for msg, spec := range specs { 153 t.Run(msg, func(t *testing.T) { 154 err := spec.src.ValidateBasic() 155 if spec.expErr { 156 require.Error(t, err) 157 } else { 158 require.NoError(t, err) 159 } 160 }) 161 } 162 } 163 164 func TestValidateInstantiateContractProposal(t *testing.T) { 165 invalidAddress := "invalid address" 166 167 specs := map[string]struct { 168 src *InstantiateContractProposal 169 expErr bool 170 }{ 171 "all good": { 172 src: InstantiateContractProposalFixture(), 173 }, 174 "without admin": { 175 src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) { 176 p.Admin = "" 177 }), 178 }, 179 "without init msg": { 180 src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) { 181 p.Msg = nil 182 }), 183 expErr: true, 184 }, 185 "with invalid init msg": { 186 src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) { 187 p.Msg = []byte("not a json string") 188 }), 189 expErr: true, 190 }, 191 "without init funds": { 192 src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) { 193 p.Funds = nil 194 }), 195 }, 196 "base data missing": { 197 src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) { 198 p.Title = "" 199 }), 200 expErr: true, 201 }, 202 "run_as missing": { 203 src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) { 204 p.RunAs = "" 205 }), 206 expErr: true, 207 }, 208 "run_as invalid": { 209 src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) { 210 p.RunAs = invalidAddress 211 }), 212 expErr: true, 213 }, 214 "admin invalid": { 215 src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) { 216 p.Admin = invalidAddress 217 }), 218 expErr: true, 219 }, 220 "code id empty": { 221 src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) { 222 p.CodeID = 0 223 }), 224 expErr: true, 225 }, 226 "label empty": { 227 src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) { 228 p.Label = "" 229 }), 230 expErr: true, 231 }, 232 "init funds negative": { 233 src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) { 234 p.Funds = sdk.CoinAdapters{{Denom: "foo", Amount: sdk.NewInt(-1)}} 235 }), 236 expErr: true, 237 }, 238 "init funds with duplicates": { 239 src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) { 240 p.Funds = sdk.CoinAdapters{{Denom: "foo", Amount: sdk.NewInt(1)}, {Denom: "foo", Amount: sdk.NewInt(2)}} 241 }), 242 expErr: true, 243 }, 244 } 245 for msg, spec := range specs { 246 t.Run(msg, func(t *testing.T) { 247 err := spec.src.ValidateBasic() 248 if spec.expErr { 249 require.Error(t, err) 250 } else { 251 require.NoError(t, err) 252 } 253 }) 254 } 255 } 256 257 func TestValidateMigrateContractProposal(t *testing.T) { 258 invalidAddress := "invalid address2" 259 260 specs := map[string]struct { 261 src *MigrateContractProposal 262 expErr bool 263 }{ 264 "all good": { 265 src: MigrateContractProposalFixture(), 266 }, 267 "without migrate msg": { 268 src: MigrateContractProposalFixture(func(p *MigrateContractProposal) { 269 p.Msg = nil 270 }), 271 expErr: true, 272 }, 273 "migrate msg with invalid json": { 274 src: MigrateContractProposalFixture(func(p *MigrateContractProposal) { 275 p.Msg = []byte("not a json message") 276 }), 277 expErr: true, 278 }, 279 "base data missing": { 280 src: MigrateContractProposalFixture(func(p *MigrateContractProposal) { 281 p.Title = "" 282 }), 283 expErr: true, 284 }, 285 "contract missing": { 286 src: MigrateContractProposalFixture(func(p *MigrateContractProposal) { 287 p.Contract = "" 288 }), 289 expErr: true, 290 }, 291 "contract invalid": { 292 src: MigrateContractProposalFixture(func(p *MigrateContractProposal) { 293 p.Contract = invalidAddress 294 }), 295 expErr: true, 296 }, 297 "code id empty": { 298 src: MigrateContractProposalFixture(func(p *MigrateContractProposal) { 299 p.CodeID = 0 300 }), 301 expErr: true, 302 }, 303 } 304 for msg, spec := range specs { 305 t.Run(msg, func(t *testing.T) { 306 err := spec.src.ValidateBasic() 307 if spec.expErr { 308 require.Error(t, err) 309 } else { 310 require.NoError(t, err) 311 } 312 }) 313 } 314 } 315 316 func TestValidateSudoContractProposal(t *testing.T) { 317 invalidAddress := "invalid address" 318 319 specs := map[string]struct { 320 src *SudoContractProposal 321 expErr bool 322 }{ 323 "all good": { 324 src: SudoContractProposalFixture(), 325 }, 326 "msg is nil": { 327 src: SudoContractProposalFixture(func(p *SudoContractProposal) { 328 p.Msg = nil 329 }), 330 expErr: true, 331 }, 332 "msg with invalid json": { 333 src: SudoContractProposalFixture(func(p *SudoContractProposal) { 334 p.Msg = []byte("not a json message") 335 }), 336 expErr: true, 337 }, 338 "base data missing": { 339 src: SudoContractProposalFixture(func(p *SudoContractProposal) { 340 p.Title = "" 341 }), 342 expErr: true, 343 }, 344 "contract missing": { 345 src: SudoContractProposalFixture(func(p *SudoContractProposal) { 346 p.Contract = "" 347 }), 348 expErr: true, 349 }, 350 "contract invalid": { 351 src: SudoContractProposalFixture(func(p *SudoContractProposal) { 352 p.Contract = invalidAddress 353 }), 354 expErr: true, 355 }, 356 } 357 for msg, spec := range specs { 358 t.Run(msg, func(t *testing.T) { 359 err := spec.src.ValidateBasic() 360 if spec.expErr { 361 require.Error(t, err) 362 } else { 363 require.NoError(t, err) 364 } 365 }) 366 } 367 } 368 369 func TestValidateExecuteContractProposal(t *testing.T) { 370 invalidAddress := "invalid address" 371 372 specs := map[string]struct { 373 src *ExecuteContractProposal 374 expErr bool 375 }{ 376 "all good": { 377 src: ExecuteContractProposalFixture(), 378 }, 379 "msg is nil": { 380 src: ExecuteContractProposalFixture(func(p *ExecuteContractProposal) { 381 p.Msg = nil 382 }), 383 expErr: true, 384 }, 385 "msg with invalid json": { 386 src: ExecuteContractProposalFixture(func(p *ExecuteContractProposal) { 387 p.Msg = []byte("not a valid json message") 388 }), 389 expErr: true, 390 }, 391 "base data missing": { 392 src: ExecuteContractProposalFixture(func(p *ExecuteContractProposal) { 393 p.Title = "" 394 }), 395 expErr: true, 396 }, 397 "contract missing": { 398 src: ExecuteContractProposalFixture(func(p *ExecuteContractProposal) { 399 p.Contract = "" 400 }), 401 expErr: true, 402 }, 403 "contract invalid": { 404 src: ExecuteContractProposalFixture(func(p *ExecuteContractProposal) { 405 p.Contract = invalidAddress 406 }), 407 expErr: true, 408 }, 409 "run as is invalid": { 410 src: ExecuteContractProposalFixture(func(p *ExecuteContractProposal) { 411 p.RunAs = invalidAddress 412 }), 413 expErr: true, 414 }, 415 } 416 for msg, spec := range specs { 417 t.Run(msg, func(t *testing.T) { 418 err := spec.src.ValidateBasic() 419 if spec.expErr { 420 require.Error(t, err) 421 } else { 422 require.NoError(t, err) 423 } 424 }) 425 } 426 } 427 428 func TestValidateUpdateAdminProposal(t *testing.T) { 429 invalidAddress := "invalid address" 430 431 specs := map[string]struct { 432 src *UpdateAdminProposal 433 expErr bool 434 }{ 435 "all good": { 436 src: UpdateAdminProposalFixture(), 437 }, 438 "base data missing": { 439 src: UpdateAdminProposalFixture(func(p *UpdateAdminProposal) { 440 p.Title = "" 441 }), 442 expErr: true, 443 }, 444 "contract missing": { 445 src: UpdateAdminProposalFixture(func(p *UpdateAdminProposal) { 446 p.Contract = "" 447 }), 448 expErr: true, 449 }, 450 "contract invalid": { 451 src: UpdateAdminProposalFixture(func(p *UpdateAdminProposal) { 452 p.Contract = invalidAddress 453 }), 454 expErr: true, 455 }, 456 "admin missing": { 457 src: UpdateAdminProposalFixture(func(p *UpdateAdminProposal) { 458 p.NewAdmin = "" 459 }), 460 expErr: true, 461 }, 462 "admin invalid": { 463 src: UpdateAdminProposalFixture(func(p *UpdateAdminProposal) { 464 p.NewAdmin = invalidAddress 465 }), 466 expErr: true, 467 }, 468 } 469 for msg, spec := range specs { 470 t.Run(msg, func(t *testing.T) { 471 err := spec.src.ValidateBasic() 472 if spec.expErr { 473 require.Error(t, err) 474 } else { 475 require.NoError(t, err) 476 } 477 }) 478 } 479 } 480 481 func TestValidateClearAdminProposal(t *testing.T) { 482 invalidAddress := "invalid address" 483 484 specs := map[string]struct { 485 src *ClearAdminProposal 486 expErr bool 487 }{ 488 "all good": { 489 src: ClearAdminProposalFixture(), 490 }, 491 "base data missing": { 492 src: ClearAdminProposalFixture(func(p *ClearAdminProposal) { 493 p.Title = "" 494 }), 495 expErr: true, 496 }, 497 "contract missing": { 498 src: ClearAdminProposalFixture(func(p *ClearAdminProposal) { 499 p.Contract = "" 500 }), 501 expErr: true, 502 }, 503 "contract invalid": { 504 src: ClearAdminProposalFixture(func(p *ClearAdminProposal) { 505 p.Contract = invalidAddress 506 }), 507 expErr: true, 508 }, 509 } 510 for msg, spec := range specs { 511 t.Run(msg, func(t *testing.T) { 512 err := spec.src.ValidateBasic() 513 if spec.expErr { 514 require.Error(t, err) 515 } else { 516 require.NoError(t, err) 517 } 518 }) 519 } 520 } 521 522 func TestProposalStrings(t *testing.T) { 523 specs := map[string]struct { 524 src govtypes.Content 525 exp string 526 }{ 527 "store code": { 528 src: StoreCodeProposalFixture(func(p *StoreCodeProposal) { 529 p.WASMByteCode = []byte{0o1, 0o2, 0o3, 0o4, 0o5, 0o6, 0o7, 0x08, 0x09, 0x0a} 530 }), 531 exp: `Store Code Proposal: 532 Title: Foo 533 Description: Bar 534 Run as: cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4 535 WasmCode: 0102030405060708090A 536 `, 537 }, 538 "instantiate contract": { 539 src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) { 540 p.Funds = sdk.CoinAdapters{{Denom: "foo", Amount: sdk.NewInt(1)}, {Denom: "bar", Amount: sdk.NewInt(2)}} 541 }), 542 exp: `Instantiate Code Proposal: 543 Title: Foo 544 Description: Bar 545 Run as: cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4 546 Admin: cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4 547 Code id: 1 548 Label: testing 549 Msg: "{\"verifier\":\"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4\",\"beneficiary\":\"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4\"}" 550 Funds: 1foo,2bar 551 `, 552 }, 553 "instantiate contract without funds": { 554 src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) { p.Funds = nil }), 555 exp: `Instantiate Code Proposal: 556 Title: Foo 557 Description: Bar 558 Run as: cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4 559 Admin: cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4 560 Code id: 1 561 Label: testing 562 Msg: "{\"verifier\":\"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4\",\"beneficiary\":\"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4\"}" 563 Funds: 564 `, 565 }, 566 "instantiate contract without admin": { 567 src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) { p.Admin = "" }), 568 exp: `Instantiate Code Proposal: 569 Title: Foo 570 Description: Bar 571 Run as: cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4 572 Admin: 573 Code id: 1 574 Label: testing 575 Msg: "{\"verifier\":\"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4\",\"beneficiary\":\"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4\"}" 576 Funds: 577 `, 578 }, 579 "migrate contract": { 580 src: MigrateContractProposalFixture(), 581 exp: `Migrate Contract Proposal: 582 Title: Foo 583 Description: Bar 584 Contract: cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr 585 Code id: 1 586 Msg: "{\"verifier\":\"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4\"}" 587 `, 588 }, 589 "update admin": { 590 src: UpdateAdminProposalFixture(), 591 exp: `Update Contract Admin Proposal: 592 Title: Foo 593 Description: Bar 594 Contract: cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr 595 New Admin: cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4 596 `, 597 }, 598 "clear admin": { 599 src: ClearAdminProposalFixture(), 600 exp: `Clear Contract Admin Proposal: 601 Title: Foo 602 Description: Bar 603 Contract: cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr 604 `, 605 }, 606 "pin codes": { 607 src: &PinCodesProposal{ 608 Title: "Foo", 609 Description: "Bar", 610 CodeIDs: []uint64{1, 2, 3}, 611 }, 612 exp: `Pin Wasm Codes Proposal: 613 Title: Foo 614 Description: Bar 615 Codes: [1 2 3] 616 `, 617 }, 618 "unpin codes": { 619 src: &UnpinCodesProposal{ 620 Title: "Foo", 621 Description: "Bar", 622 CodeIDs: []uint64{3, 2, 1}, 623 }, 624 exp: `Unpin Wasm Codes Proposal: 625 Title: Foo 626 Description: Bar 627 Codes: [3 2 1] 628 `, 629 }, 630 "update deployment whitelist": { 631 src: &UpdateDeploymentWhitelistProposal{ 632 Title: "Foo", 633 Description: "Bar", 634 DistributorAddrs: []string{"fb1cftp8q8g4aa65nw9s5trwexe77d9t6cr8ndu02", "ex10q0rk5qnyag7wfvvt7rtphlw589m7frs3hvqmf"}, 635 }, 636 exp: `title:"Foo" description:"Bar" distributorAddrs:"fb1cftp8q8g4aa65nw9s5trwexe77d9t6cr8ndu02" distributorAddrs:"ex10q0rk5qnyag7wfvvt7rtphlw589m7frs3hvqmf" `, 637 }, 638 } 639 for msg, spec := range specs { 640 t.Run(msg, func(t *testing.T) { 641 assert.Equal(t, spec.exp, spec.src.String()) 642 }) 643 } 644 } 645 646 func TestProposalYaml(t *testing.T) { 647 specs := map[string]struct { 648 src govtypes.Content 649 exp string 650 }{ 651 "store code": { 652 src: StoreCodeProposalFixture(func(p *StoreCodeProposal) { 653 p.WASMByteCode = []byte{0o1, 0o2, 0o3, 0o4, 0o5, 0o6, 0o7, 0x08, 0x09, 0x0a} 654 }), 655 exp: `title: Foo 656 description: Bar 657 run_as: cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4 658 wasm_byte_code: AQIDBAUGBwgJCg== 659 instantiate_permission: null 660 `, 661 }, 662 "instantiate contract": { 663 src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) { 664 p.Funds = sdk.CoinAdapters{{Denom: "foo", Amount: sdk.NewInt(1)}, {Denom: "bar", Amount: sdk.NewInt(2)}} 665 }), 666 exp: `title: Foo 667 description: Bar 668 run_as: cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4 669 admin: cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4 670 code_id: 1 671 label: testing 672 msg: '{"verifier":"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4","beneficiary":"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4"}' 673 funds: 674 - denom: foo 675 amount: "0.000000000000000001" 676 - denom: bar 677 amount: "0.000000000000000002" 678 `, 679 }, 680 "instantiate contract without funds": { 681 src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) { p.Funds = nil }), 682 exp: `title: Foo 683 description: Bar 684 run_as: cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4 685 admin: cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4 686 code_id: 1 687 label: testing 688 msg: '{"verifier":"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4","beneficiary":"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4"}' 689 funds: [] 690 `, 691 }, 692 "instantiate contract without admin": { 693 src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) { p.Admin = "" }), 694 exp: `title: Foo 695 description: Bar 696 run_as: cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4 697 admin: "" 698 code_id: 1 699 label: testing 700 msg: '{"verifier":"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4","beneficiary":"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4"}' 701 funds: [] 702 `, 703 }, 704 "migrate contract": { 705 src: MigrateContractProposalFixture(), 706 exp: `title: Foo 707 description: Bar 708 contract: cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr 709 code_id: 1 710 msg: '{"verifier":"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4"}' 711 `, 712 }, 713 "update admin": { 714 src: UpdateAdminProposalFixture(), 715 exp: `title: Foo 716 description: Bar 717 new_admin: cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4 718 contract: cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr 719 `, 720 }, 721 "clear admin": { 722 src: ClearAdminProposalFixture(), 723 exp: `title: Foo 724 description: Bar 725 contract: cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr 726 `, 727 }, 728 "pin codes": { 729 src: &PinCodesProposal{ 730 Title: "Foo", 731 Description: "Bar", 732 CodeIDs: []uint64{1, 2, 3}, 733 }, 734 exp: `title: Foo 735 description: Bar 736 code_ids: 737 - 1 738 - 2 739 - 3 740 `, 741 }, 742 "update deployment whitelist": { 743 src: &UpdateDeploymentWhitelistProposal{ 744 Title: "Foo", 745 Description: "Bar", 746 DistributorAddrs: []string{"fb1cftp8q8g4aa65nw9s5trwexe77d9t6cr8ndu02", "ex10q0rk5qnyag7wfvvt7rtphlw589m7frs3hvqmf"}, 747 }, 748 exp: `title: Foo 749 description: Bar 750 distributor_addresses: 751 - fb1cftp8q8g4aa65nw9s5trwexe77d9t6cr8ndu02 752 - ex10q0rk5qnyag7wfvvt7rtphlw589m7frs3hvqmf 753 `, 754 }, 755 } 756 for msg, spec := range specs { 757 t.Run(msg, func(t *testing.T) { 758 v, err := yaml.Marshal(&spec.src) 759 require.NoError(t, err) 760 assert.Equal(t, spec.exp, string(v)) 761 }) 762 } 763 } 764 765 func TestConvertToProposals(t *testing.T) { 766 cases := map[string]struct { 767 input string 768 isError bool 769 proposals []ProposalType 770 }{ 771 "one proper item": { 772 input: "UpdateAdmin", 773 proposals: []ProposalType{ProposalTypeUpdateAdmin}, 774 }, 775 "multiple proper items": { 776 input: "StoreCode,InstantiateContract,MigrateContract,UpdateDeploymentWhitelist", 777 proposals: []ProposalType{ProposalTypeStoreCode, ProposalTypeInstantiateContract, ProposalTypeMigrateContract, ProposalTypeUpdateDeploymentWhitelist}, 778 }, 779 "empty trailing item": { 780 input: "StoreCode,", 781 isError: true, 782 }, 783 "invalid item": { 784 input: "StoreCode,InvalidProposalType", 785 isError: true, 786 }, 787 } 788 789 for name, tc := range cases { 790 t.Run(name, func(t *testing.T) { 791 chunks := strings.Split(tc.input, ",") 792 proposals, err := ConvertToProposals(chunks) 793 if tc.isError { 794 require.Error(t, err) 795 } else { 796 require.NoError(t, err) 797 require.Equal(t, proposals, tc.proposals) 798 } 799 }) 800 } 801 } 802 803 func TestUnmarshalContentFromJson(t *testing.T) { 804 specs := map[string]struct { 805 src string 806 got govtypes.Content 807 exp govtypes.Content 808 }{ 809 "instantiate ": { 810 src: ` 811 { 812 "title": "foo", 813 "description": "bar", 814 "admin": "myAdminAddress", 815 "code_id": 1, 816 "funds": [{"denom": "ALX", "amount": "2"},{"denom": "BLX","amount": "3"}], 817 "msg": {}, 818 "label": "testing", 819 "run_as": "myRunAsAddress" 820 }`, 821 got: &InstantiateContractProposal{}, 822 exp: &InstantiateContractProposal{ 823 Title: "foo", 824 Description: "bar", 825 RunAs: "myRunAsAddress", 826 Admin: "myAdminAddress", 827 CodeID: 1, 828 Label: "testing", 829 Msg: []byte("{}"), 830 Funds: sdk.CoinAdapters{{"ALX", sdk.NewInt(2)}, {"BLX", sdk.NewInt(3)}}, 831 }, 832 }, 833 "migrate ": { 834 src: ` 835 { 836 "title": "foo", 837 "description": "bar", 838 "code_id": 1, 839 "contract": "myContractAddr", 840 "msg": {}, 841 "run_as": "myRunAsAddress" 842 }`, 843 got: &MigrateContractProposal{}, 844 exp: &MigrateContractProposal{ 845 Title: "foo", 846 Description: "bar", 847 Contract: "myContractAddr", 848 CodeID: 1, 849 Msg: []byte("{}"), 850 }, 851 }, 852 "update deployment whitelit": { 853 src: ` 854 { 855 "title": "foo", 856 "description": "bar", 857 "distributorAddrs": ["fb1cftp8q8g4aa65nw9s5trwexe77d9t6cr8ndu02", "ex10q0rk5qnyag7wfvvt7rtphlw589m7frs3hvqmf"] 858 }`, 859 got: &UpdateDeploymentWhitelistProposal{}, 860 exp: &UpdateDeploymentWhitelistProposal{ 861 Title: "foo", 862 Description: "bar", 863 DistributorAddrs: []string{"fb1cftp8q8g4aa65nw9s5trwexe77d9t6cr8ndu02", "ex10q0rk5qnyag7wfvvt7rtphlw589m7frs3hvqmf"}, 864 }, 865 }, 866 } 867 for name, spec := range specs { 868 t.Run(name, func(t *testing.T) { 869 require.NoError(t, json.Unmarshal([]byte(spec.src), spec.got)) 870 assert.Equal(t, spec.exp, spec.got) 871 }) 872 } 873 } 874 875 func TestProposalJsonSignBytes(t *testing.T) { 876 const myInnerMsg = `{"foo":"bar"}` 877 specs := map[string]struct { 878 src govtypes.Content 879 exp string 880 }{ 881 "instantiate contract": { 882 src: &InstantiateContractProposal{Msg: RawContractMessage(myInnerMsg)}, 883 exp: ` 884 { 885 "type":"fbexchain/gov/MsgSubmitProposal", 886 "value":{"content":{"type":"wasm/InstantiateContractProposal","value":{"funds":[],"msg":{"foo":"bar"}}},"initial_deposit":[],"proposer":""} 887 }`, 888 }, 889 "migrate contract": { 890 src: &MigrateContractProposal{Msg: RawContractMessage(myInnerMsg)}, 891 exp: ` 892 { 893 "type":"fbexchain/gov/MsgSubmitProposal", 894 "value":{"content":{"type":"wasm/MigrateContractProposal","value":{"msg":{"foo":"bar"}}},"initial_deposit":[],"proposer":""} 895 }`, 896 }, 897 "update wasm deployment whitelist": { 898 src: &UpdateDeploymentWhitelistProposal{DistributorAddrs: []string{"fb1cftp8q8g4aa65nw9s5trwexe77d9t6cr8ndu02", "ex10q0rk5qnyag7wfvvt7rtphlw589m7frs3hvqmf"}}, 899 exp: ` 900 { 901 "type":"fbexchain/gov/MsgSubmitProposal", 902 "value":{"content":{"type":"wasm/UpdateDeploymentWhitelistProposal","value":{"distributorAddrs":["fb1cftp8q8g4aa65nw9s5trwexe77d9t6cr8ndu02","ex10q0rk5qnyag7wfvvt7rtphlw589m7frs3hvqmf"]}},"initial_deposit":[],"proposer":""} 903 }`, 904 }, 905 } 906 for name, spec := range specs { 907 t.Run(name, func(t *testing.T) { 908 msg := govtypes.NewMsgSubmitProposal(spec.src, sdk.NewCoins(), []byte{}) 909 910 bz := msg.GetSignBytes() 911 assert.JSONEq(t, spec.exp, string(bz), "raw: %s", string(bz)) 912 }) 913 } 914 }