github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/ledger/trie_test.go (about) 1 package ledger 2 3 import ( 4 "encoding/json" 5 "testing" 6 7 "github.com/fxamacker/cbor/v2" 8 "github.com/stretchr/testify/require" 9 10 "github.com/onflow/flow-go/model/flow" 11 ) 12 13 // TestPayloadEquals tests equality of payloads. It tests: 14 // - equality of empty, nil, and not-empty payloads 15 // - equality of payloads with different keys and same value 16 // - equality of payloads with same key and different values 17 // - and etc. 18 func TestPayloadEquals(t *testing.T) { 19 nilPayload := (*Payload)(nil) 20 emptyPayload := EmptyPayload() 21 22 t.Run("nil vs empty", func(t *testing.T) { 23 require.True(t, nilPayload.Equals(emptyPayload)) 24 require.True(t, emptyPayload.Equals(nilPayload)) 25 }) 26 27 t.Run("nil vs nil", func(t *testing.T) { 28 require.True(t, nilPayload.Equals(nilPayload)) 29 }) 30 31 t.Run("empty vs empty", func(t *testing.T) { 32 require.True(t, emptyPayload.Equals(emptyPayload)) 33 }) 34 35 t.Run("empty vs non-empty", func(t *testing.T) { 36 p := NewPayload( 37 Key{KeyParts: []KeyPart{{1, []byte{0x01, 0x02}}}}, 38 []byte{0x03, 0x04}, 39 ) 40 require.False(t, emptyPayload.Equals(p)) 41 require.False(t, p.Equals(emptyPayload)) 42 }) 43 44 t.Run("nil vs non-empty", func(t *testing.T) { 45 p := NewPayload( 46 Key{KeyParts: []KeyPart{{1, []byte{0x01, 0x02}}}}, 47 []byte{0x03, 0x04}, 48 ) 49 require.False(t, nilPayload.Equals(p)) 50 require.False(t, p.Equals(nilPayload)) 51 }) 52 53 t.Run("different key same value", func(t *testing.T) { 54 value := []byte{0x03, 0x04} 55 56 p := NewPayload( 57 Key{KeyParts: []KeyPart{{1, []byte{0x01, 0x02}}}}, 58 value, 59 ) 60 // p1.Key.KeyParts[0].Type is different 61 p1 := NewPayload( 62 Key{KeyParts: []KeyPart{{2, []byte{0x01, 0x02}}}}, 63 value, 64 ) 65 // p2.Key.KeyParts[0].Value is different 66 p2 := NewPayload( 67 Key{KeyParts: []KeyPart{{1, []byte{0x01, 0x02, 0x03}}}}, 68 value, 69 ) 70 // len(p3.Key.KeyParts) is different 71 p3 := NewPayload( 72 Key{KeyParts: []KeyPart{ 73 {1, []byte{0x01, 0x02}}, 74 {2, []byte{0x03, 0x04}}}, 75 }, 76 value, 77 ) 78 require.False(t, p.Equals(p1)) 79 require.False(t, p.Equals(p2)) 80 require.False(t, p.Equals(p3)) 81 }) 82 83 t.Run("different key empty value", func(t *testing.T) { 84 value := []byte{} 85 86 p := NewPayload( 87 Key{KeyParts: []KeyPart{{1, []byte{0x01, 0x02}}}}, 88 value, 89 ) 90 // p1.Key.KeyParts[0].Type is different 91 p1 := NewPayload( 92 Key{KeyParts: []KeyPart{{2, []byte{0x01, 0x02}}}}, 93 value, 94 ) 95 // p2.Key.KeyParts[0].Value is different 96 p2 := NewPayload( 97 Key{KeyParts: []KeyPart{{1, []byte{0x01, 0x02, 0x03}}}}, 98 value, 99 ) 100 // len(p3.Key.KeyParts) is different 101 p3 := NewPayload( 102 Key{KeyParts: []KeyPart{ 103 {1, []byte{0x01, 0x02}}, 104 {2, []byte{0x03, 0x04}}}, 105 }, 106 value, 107 ) 108 require.False(t, p.Equals(p1)) 109 require.False(t, p.Equals(p2)) 110 require.False(t, p.Equals(p3)) 111 }) 112 113 t.Run("same key different value", func(t *testing.T) { 114 key := Key{KeyParts: []KeyPart{{1, []byte{0x01, 0x02}}}} 115 116 p := NewPayload( 117 key, 118 []byte{0x03, 0x04}, 119 ) 120 // p1.Value is nil 121 p1 := NewPayload( 122 key, 123 nil, 124 ) 125 // p2.Value is empty 126 p2 := NewPayload( 127 key, 128 []byte{}, 129 ) 130 // p3.Value length is different 131 p3 := NewPayload( 132 key, 133 []byte{0x03}, 134 ) 135 // p4.Value data is different 136 p4 := NewPayload( 137 key, 138 []byte{0x03, 0x05}, 139 ) 140 require.False(t, p.Equals(p1)) 141 require.False(t, p.Equals(p2)) 142 require.False(t, p.Equals(p3)) 143 require.False(t, p.Equals(p4)) 144 }) 145 146 t.Run("same key same value", func(t *testing.T) { 147 p1 := NewPayload( 148 Key{KeyParts: []KeyPart{{1, []byte{0x01, 0x02}}}}, 149 []byte{0x03, 0x04}, 150 ) 151 p2 := NewPayload( 152 Key{KeyParts: []KeyPart{{1, []byte{0x01, 0x02}}}}, 153 []byte{0x03, 0x04}, 154 ) 155 require.True(t, p1.Equals(p2)) 156 require.True(t, p2.Equals(p1)) 157 }) 158 } 159 160 // TestPayloadValueEquals tests equality of payload values. It tests: 161 // - equality of empty, nil, and not-empty payloads 162 // - equality of payloads with different keys and same value 163 // - equality of payloads with same key and different values 164 // - and etc. 165 func TestPayloadValuEquals(t *testing.T) { 166 nilPayload := (*Payload)(nil) 167 emptyPayload := EmptyPayload() 168 169 t.Run("nil vs empty", func(t *testing.T) { 170 require.True(t, nilPayload.ValueEquals(emptyPayload)) 171 require.True(t, emptyPayload.ValueEquals(nilPayload)) 172 }) 173 174 t.Run("nil vs nil", func(t *testing.T) { 175 require.True(t, nilPayload.ValueEquals(nilPayload)) 176 }) 177 178 t.Run("empty vs empty", func(t *testing.T) { 179 require.True(t, emptyPayload.ValueEquals(emptyPayload)) 180 }) 181 182 t.Run("empty vs non-empty", func(t *testing.T) { 183 p := NewPayload( 184 Key{KeyParts: []KeyPart{{1, []byte{0x01, 0x02}}}}, 185 []byte{0x03, 0x04}, 186 ) 187 require.False(t, emptyPayload.ValueEquals(p)) 188 require.False(t, p.ValueEquals(emptyPayload)) 189 }) 190 191 t.Run("nil vs non-empty", func(t *testing.T) { 192 p := NewPayload( 193 Key{KeyParts: []KeyPart{{1, []byte{0x01, 0x02}}}}, 194 []byte{0x03, 0x04}, 195 ) 196 require.False(t, nilPayload.ValueEquals(p)) 197 require.False(t, p.ValueEquals(nilPayload)) 198 }) 199 200 t.Run("different key same value", func(t *testing.T) { 201 value := []byte{0x03, 0x04} 202 203 p := NewPayload( 204 Key{KeyParts: []KeyPart{{1, []byte{0x01, 0x02}}}}, 205 value, 206 ) 207 // p1.Key.KeyParts[0].Type is different 208 p1 := NewPayload( 209 Key{KeyParts: []KeyPart{{2, []byte{0x01, 0x02}}}}, 210 value, 211 ) 212 // p2.Key.KeyParts[0].Value is different 213 p2 := NewPayload( 214 Key{KeyParts: []KeyPart{{1, []byte{0x01, 0x02, 0x03}}}}, 215 value, 216 ) 217 // len(p3.Key.KeyParts) is different 218 p3 := NewPayload( 219 Key{KeyParts: []KeyPart{ 220 {1, []byte{0x01, 0x02}}, 221 {2, []byte{0x03, 0x04}}}, 222 }, 223 value, 224 ) 225 require.True(t, p.ValueEquals(p1)) 226 require.True(t, p.ValueEquals(p2)) 227 require.True(t, p.ValueEquals(p3)) 228 }) 229 230 t.Run("different key empty value", func(t *testing.T) { 231 value := []byte{} 232 233 p := NewPayload( 234 Key{KeyParts: []KeyPart{{1, []byte{0x01, 0x02}}}}, 235 value, 236 ) 237 // p1.Key.KeyParts[0].Type is different 238 p1 := NewPayload( 239 Key{KeyParts: []KeyPart{{2, []byte{0x01, 0x02}}}}, 240 value, 241 ) 242 // p2.Key.KeyParts[0].Value is different 243 p2 := NewPayload( 244 Key{KeyParts: []KeyPart{{1, []byte{0x01, 0x02, 0x03}}}}, 245 value, 246 ) 247 // len(p3.Key.KeyParts) is different 248 p3 := NewPayload( 249 Key{KeyParts: []KeyPart{ 250 {1, []byte{0x01, 0x02}}, 251 {2, []byte{0x03, 0x04}}}, 252 }, 253 value, 254 ) 255 require.True(t, p.ValueEquals(p1)) 256 require.True(t, p.ValueEquals(p2)) 257 require.True(t, p.ValueEquals(p3)) 258 }) 259 260 t.Run("same key different value", func(t *testing.T) { 261 key := Key{KeyParts: []KeyPart{{1, []byte{0x01, 0x02}}}} 262 263 p := NewPayload( 264 key, 265 []byte{0x03, 0x04}, 266 ) 267 // p1.Value is nil 268 p1 := NewPayload( 269 key, 270 nil, 271 ) 272 // p2.Value is empty 273 p2 := NewPayload( 274 key, 275 []byte{}, 276 ) 277 // p3.Value length is different 278 p3 := NewPayload( 279 key, 280 []byte{0x03}, 281 ) 282 // p4.Value data is different 283 p4 := NewPayload( 284 key, 285 []byte{0x03, 0x05}, 286 ) 287 require.False(t, p.ValueEquals(p1)) 288 require.False(t, p.ValueEquals(p2)) 289 require.False(t, p.ValueEquals(p3)) 290 require.False(t, p.ValueEquals(p4)) 291 }) 292 293 t.Run("same key same value", func(t *testing.T) { 294 p1 := NewPayload( 295 Key{KeyParts: []KeyPart{{1, []byte{0x01, 0x02}}}}, 296 []byte{0x03, 0x04}, 297 ) 298 p2 := NewPayload( 299 Key{KeyParts: []KeyPart{{1, []byte{0x01, 0x02}}}}, 300 []byte{0x03, 0x04}, 301 ) 302 require.True(t, p1.ValueEquals(p2)) 303 require.True(t, p2.ValueEquals(p1)) 304 }) 305 } 306 307 func TestPayloadKey(t *testing.T) { 308 t.Run("nil payload", func(t *testing.T) { 309 var p *Payload 310 k, err := p.Key() 311 require.NoError(t, err) 312 require.Equal(t, Key{}, k) 313 314 _, err = p.Address() 315 require.Error(t, err) 316 }) 317 t.Run("empty payload", func(t *testing.T) { 318 p := Payload{} 319 k, err := p.Key() 320 require.NoError(t, err) 321 require.Equal(t, Key{}, k) 322 323 _, err = p.Address() 324 require.Error(t, err) 325 }) 326 t.Run("empty key", func(t *testing.T) { 327 p := NewPayload(Key{}, Value{}) 328 k, err := p.Key() 329 require.NoError(t, err) 330 require.Equal(t, Key{}, k) 331 332 _, err = p.Address() 333 require.Error(t, err) 334 }) 335 t.Run("global key", func(t *testing.T) { 336 key := Key{KeyParts: []KeyPart{{Type: 0, Value: []byte{}}, {Type: 1, Value: []byte("def")}}} 337 value := Value([]byte{0, 1, 2}) 338 p := NewPayload(key, value) 339 k, err := p.Key() 340 require.NoError(t, err) 341 require.Equal(t, key, k) 342 343 addr, err := p.Address() 344 require.NoError(t, err) 345 require.Equal(t, flow.EmptyAddress, addr) 346 }) 347 t.Run("key", func(t *testing.T) { 348 address := []byte{1, 2, 3, 4, 5, 6, 7, 8} 349 key := Key{KeyParts: []KeyPart{{Type: 0, Value: address}, {Type: 1, Value: []byte("def")}}} 350 value := Value([]byte{0, 1, 2}) 351 p := NewPayload(key, value) 352 k, err := p.Key() 353 require.NoError(t, err) 354 require.Equal(t, key, k) 355 356 addr, err := p.Address() 357 require.NoError(t, err) 358 require.Equal(t, flow.Address(address), addr) 359 }) 360 } 361 362 func TestPayloadValue(t *testing.T) { 363 t.Run("nil payload", func(t *testing.T) { 364 var p *Payload 365 require.Equal(t, 0, p.Value().Size()) 366 }) 367 t.Run("empty payload", func(t *testing.T) { 368 p := Payload{} 369 require.Equal(t, 0, p.Value().Size()) 370 }) 371 t.Run("empty value", func(t *testing.T) { 372 key := Key{KeyParts: []KeyPart{{Type: 0, Value: []byte("abc")}, {Type: 1, Value: []byte("def")}}} 373 p := NewPayload(key, Value{}) 374 require.Equal(t, 0, p.Value().Size()) 375 }) 376 t.Run("value", func(t *testing.T) { 377 key := Key{KeyParts: []KeyPart{{Type: 0, Value: []byte("abc")}, {Type: 1, Value: []byte("def")}}} 378 value := Value([]byte{0, 1, 2}) 379 p := NewPayload(key, value) 380 require.Equal(t, value, p.Value()) 381 }) 382 } 383 384 func TestPayloadJSONSerialization(t *testing.T) { 385 t.Run("nil payload", func(t *testing.T) { 386 encoded := []byte("null") 387 388 var p *Payload 389 b, err := json.Marshal(p) 390 require.NoError(t, err) 391 require.Equal(t, encoded, b) 392 393 var p2 *Payload 394 err = json.Unmarshal(b, &p2) 395 require.NoError(t, err) 396 require.Equal(t, p, p2) 397 }) 398 399 t.Run("empty payload", func(t *testing.T) { 400 encoded := []byte(`{"Key":{"KeyParts":null},"Value":""}`) 401 402 var p Payload 403 b, err := json.Marshal(p) 404 require.NoError(t, err) 405 require.Equal(t, encoded, b) 406 407 var p2 Payload 408 err = json.Unmarshal(b, &p2) 409 require.NoError(t, err) 410 411 // Reset b to make sure that p2 doesn't share underlying data with JSON-encoded data. 412 for i := 0; i < len(b); i++ { 413 b[i] = 0 414 } 415 416 require.True(t, p2.IsEmpty()) 417 418 k2, err := p2.Key() 419 require.NoError(t, err) 420 require.True(t, k2.Equals(&Key{})) 421 422 v2 := p2.Value() 423 require.True(t, v2.Equals(Value{})) 424 }) 425 426 t.Run("empty key", func(t *testing.T) { 427 encoded := []byte(`{"Key":{"KeyParts":null},"Value":"000102"}`) 428 429 k := Key{} 430 v := []byte{0, 1, 2} 431 p := NewPayload(k, v) 432 b, err := json.Marshal(p) 433 require.NoError(t, err) 434 require.Equal(t, encoded, b) 435 436 var p2 Payload 437 err = json.Unmarshal(b, &p2) 438 require.NoError(t, err) 439 440 // Reset b to make sure that p2 doesn't share underlying data with JSON-encoded data. 441 for i := 0; i < len(b); i++ { 442 b[i] = 0 443 } 444 445 require.True(t, p.Equals(&p2)) 446 447 k2, err := p2.Key() 448 require.NoError(t, err) 449 require.True(t, k2.Equals(&k)) 450 451 v2 := p2.Value() 452 require.True(t, v2.Equals(v)) 453 }) 454 455 t.Run("empty value", func(t *testing.T) { 456 encoded := []byte(`{"Key":{"KeyParts":[{"Type":1,"Value":"0102"}]},"Value":""}`) 457 458 k := Key{KeyParts: []KeyPart{{1, []byte{1, 2}}}} 459 var v Value 460 p := NewPayload(k, v) 461 b, err := json.Marshal(p) 462 require.NoError(t, err) 463 require.Equal(t, encoded, b) 464 465 var p2 Payload 466 err = json.Unmarshal(b, &p2) 467 require.NoError(t, err) 468 469 // Reset b to make sure that p2 doesn't share underlying data with JSON-encoded data. 470 for i := 0; i < len(b); i++ { 471 b[i] = 0 472 } 473 474 require.True(t, p.Equals(&p2)) 475 476 k2, err := p2.Key() 477 require.NoError(t, err) 478 require.True(t, k2.Equals(&k)) 479 480 v2 := p2.Value() 481 require.True(t, v2.Equals(v)) 482 }) 483 484 t.Run("payload", func(t *testing.T) { 485 encoded := []byte(`{"Key":{"KeyParts":[{"Type":1,"Value":"0102"}]},"Value":"030405"}`) 486 487 k := Key{KeyParts: []KeyPart{{1, []byte{1, 2}}}} 488 v := Value{3, 4, 5} 489 p := NewPayload(k, v) 490 b, err := json.Marshal(p) 491 require.NoError(t, err) 492 require.Equal(t, []byte(encoded), b) 493 494 var p2 Payload 495 err = json.Unmarshal(b, &p2) 496 require.NoError(t, err) 497 498 // Reset b to make sure that p2 doesn't share underlying data with JSON-encoded data. 499 for i := 0; i < len(b); i++ { 500 b[i] = 0 501 } 502 503 require.True(t, p.Equals(&p2)) 504 505 k2, err := p2.Key() 506 require.NoError(t, err) 507 require.True(t, k2.Equals(&k)) 508 509 v2 := p2.Value() 510 require.True(t, v2.Equals(v)) 511 }) 512 } 513 514 func TestPayloadCBORSerialization(t *testing.T) { 515 t.Run("nil payload", func(t *testing.T) { 516 encoded := []byte{0xf6} // null 517 518 var p *Payload 519 b, err := cbor.Marshal(p) 520 require.NoError(t, err) 521 require.Equal(t, encoded, b) 522 523 var p2 *Payload 524 err = cbor.Unmarshal(b, &p2) 525 require.NoError(t, err) 526 require.Equal(t, p, p2) 527 }) 528 529 t.Run("empty payload", func(t *testing.T) { 530 encoded := []byte{ 531 0xa2, // map(2) 532 0x63, // text(3) 533 0x4b, 0x65, 0x79, // "Key" 534 0xa1, // map(1) 535 0x68, // text(8) 536 0x4b, 0x65, 0x79, 0x50, 0x61, 0x72, 0x74, 0x73, // "KeyParts" 537 0xf6, // null 538 0x65, // text(5) 539 0x56, 0x61, 0x6c, 0x75, 0x65, // "Value" 540 0xf6, // null 541 } 542 543 var p Payload 544 b, err := cbor.Marshal(p) 545 require.NoError(t, err) 546 require.Equal(t, encoded, b) 547 548 var p2 Payload 549 err = cbor.Unmarshal(b, &p2) 550 require.NoError(t, err) 551 552 // Reset b to make sure that p2 doesn't share underlying data with CBOR-encoded data. 553 for i := 0; i < len(b); i++ { 554 b[i] = 0 555 } 556 557 require.True(t, p2.IsEmpty()) 558 559 k2, err := p2.Key() 560 require.NoError(t, err) 561 require.True(t, k2.Equals(&Key{})) 562 563 v2 := p2.Value() 564 require.True(t, v2.Equals(Value{})) 565 }) 566 567 t.Run("empty key", func(t *testing.T) { 568 encoded := []byte{ 569 0xa2, // map(2) 570 0x63, // text(3) 571 0x4b, 0x65, 0x79, // "Key" 572 0xa1, // map(1) 573 0x68, // text(8) 574 0x4b, 0x65, 0x79, 0x50, 0x61, 0x72, 0x74, 0x73, // "KeyParts" 575 0xf6, // null 576 0x65, // text(5) 577 0x56, 0x61, 0x6c, 0x75, 0x65, // "Value" 578 0x43, // bytes(3) 579 0x00, 0x01, 0x02, // "\u0000\u0001\u0002" 580 } 581 582 k := Key{} 583 v := []byte{0, 1, 2} 584 p := NewPayload(k, v) 585 b, err := cbor.Marshal(p) 586 require.NoError(t, err) 587 require.Equal(t, encoded, b) 588 589 var p2 Payload 590 err = cbor.Unmarshal(b, &p2) 591 require.NoError(t, err) 592 593 // Reset b to make sure that p2 doesn't share underlying data with CBOR-encoded data. 594 for i := 0; i < len(b); i++ { 595 b[i] = 0 596 } 597 598 require.True(t, p.Equals(&p2)) 599 600 k2, err := p2.Key() 601 require.NoError(t, err) 602 require.True(t, k2.Equals(&k)) 603 604 v2 := p2.Value() 605 require.True(t, v2.Equals(v)) 606 }) 607 608 t.Run("empty value", func(t *testing.T) { 609 encoded := []byte{ 610 0xa2, // map(2) 611 0x63, // text(3) 612 0x4b, 0x65, 0x79, // "Key" 613 0xa1, // map(1) 614 0x68, // text(8) 615 0x4b, 0x65, 0x79, 0x50, 0x61, 0x72, 0x74, 0x73, // "KeyParts" 616 0x81, // array(1) 617 0xa2, // map(2) 618 0x64, // text(4) 619 0x54, 0x79, 0x70, 0x65, // "Type" 620 0x01, // unsigned(1) 621 0x65, // text(5) 622 0x56, 0x61, 0x6c, 0x75, 0x65, // "Value" 623 0x42, // bytes(2) 624 0x01, 0x02, // "\u0001\u0002" 625 0x65, // text(5) 626 0x56, 0x61, 0x6c, 0x75, 0x65, // "Value" 627 0xf6, // null 628 } 629 630 k := Key{KeyParts: []KeyPart{{1, []byte{1, 2}}}} 631 var v Value 632 p := NewPayload(k, v) 633 b, err := cbor.Marshal(p) 634 require.NoError(t, err) 635 require.Equal(t, encoded, b) 636 637 var p2 Payload 638 err = cbor.Unmarshal(b, &p2) 639 require.NoError(t, err) 640 641 // Reset b to make sure that p2 doesn't share underlying data with CBOR-encoded data. 642 for i := 0; i < len(b); i++ { 643 b[i] = 0 644 } 645 646 require.True(t, p.Equals(&p2)) 647 648 k2, err := p2.Key() 649 require.NoError(t, err) 650 require.True(t, k2.Equals(&k)) 651 652 v2 := p2.Value() 653 require.True(t, v2.Equals(v)) 654 }) 655 656 t.Run("payload", func(t *testing.T) { 657 encoded := []byte{ 658 0xa2, // map(2) 659 0x63, // text(3) 660 0x4b, 0x65, 0x79, // "Key" 661 0xa1, // map(1) 662 0x68, // text(8) 663 0x4b, 0x65, 0x79, 0x50, 0x61, 0x72, 0x74, 0x73, // "KeyParts" 664 0x81, // array(1) 665 0xa2, // map(2) 666 0x64, // text(4) 667 0x54, 0x79, 0x70, 0x65, // "Type" 668 0x01, // unsigned(1) 669 0x65, // text(5) 670 0x56, 0x61, 0x6c, 0x75, 0x65, // "Value" 671 0x42, // bytes(2) 672 0x01, 0x02, // "\u0001\u0002" 673 0x65, // text(5) 674 0x56, 0x61, 0x6c, 0x75, 0x65, // "Value" 675 0x43, // bytes(3) 676 0x03, 0x04, 0x05, // "\u0003\u0004\u0005" 677 } 678 679 k := Key{KeyParts: []KeyPart{{1, []byte{1, 2}}}} 680 v := Value{3, 4, 5} 681 p := NewPayload(k, v) 682 b, err := cbor.Marshal(p) 683 require.NoError(t, err) 684 require.Equal(t, []byte(encoded), b) 685 686 var p2 Payload 687 err = cbor.Unmarshal(b, &p2) 688 require.NoError(t, err) 689 690 // Reset b to make sure that p2 doesn't share underlying data with CBOR-encoded data. 691 for i := 0; i < len(b); i++ { 692 b[i] = 0 693 } 694 695 require.True(t, p.Equals(&p2)) 696 697 k2, err := p2.Key() 698 require.NoError(t, err) 699 require.True(t, k2.Equals(&k)) 700 701 v2 := p2.Value() 702 require.True(t, v2.Equals(v)) 703 }) 704 }