@rootzero/contracts 1.17.0 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/codec/Blocks.sol CHANGED
@@ -25,7 +25,10 @@ import {max32} from "../utils/Utils.sol";
25
25
  /// and reserve the complete destination region before calling them. Helpers are
26
26
  /// ordered as inspection, generic writes, specialized writes, decoding, and
27
27
  /// block factories; fixed layouts within a section are ordered from smaller to
28
- /// larger payloads.
28
+ /// larger payloads. `write*` helpers copy dynamic inputs from memory, while
29
+ /// `copy*` helpers copy dynamic inputs directly from calldata. Allocating
30
+ /// factories use the semantic block name for memory inputs and append `Copy`
31
+ /// for calldata inputs, while delegating to the corresponding writer.
29
32
  library Blocks {
30
33
  /// @dev A block header or declared payload exceeds the source region.
31
34
  error MalformedBlocks();
@@ -827,6 +830,140 @@ library Blocks {
827
830
  }
828
831
  }
829
832
 
833
+ // Calldata copy writers
834
+
835
+ /// @notice Encode a custom block at `i`, copying its payload from calldata.
836
+ /// @dev DANGER: Unchecked memory write. The caller must validate the payload
837
+ /// length and reserve `Sizes.Header + payload.length` bytes first.
838
+ function copy(bytes memory dst, uint i, bytes4 key, bytes calldata payload) internal pure {
839
+ uint len = max32(payload.length);
840
+ uint head = (uint(uint32(key)) << 224) | (len << 192);
841
+ assembly ("memory-safe") {
842
+ let p := add(add(dst, 0x20), i)
843
+ mstore(p, head)
844
+ calldatacopy(add(p, 0x08), payload.offset, len)
845
+ }
846
+ }
847
+
848
+ /// @notice Encode a LIST block at `i`, copying its payload from calldata.
849
+ function copyList(bytes memory dst, uint i, bytes calldata value) internal pure {
850
+ copy(dst, i, Keys.List, value);
851
+ }
852
+
853
+ /// @notice Encode an EVM block at `i`, copying its payload from calldata.
854
+ function copyEvm(bytes memory dst, uint i, bytes calldata value) internal pure {
855
+ copy(dst, i, Keys.Evm, value);
856
+ }
857
+
858
+ /// @notice Encode a BYTES block at `i`, copying its payload from calldata.
859
+ function copyBytes(bytes memory dst, uint i, bytes calldata value) internal pure {
860
+ copy(dst, i, Keys.Bytes, value);
861
+ }
862
+
863
+ /// @notice Encode a STRING block at `i`, copying its payload from calldata.
864
+ function copyString(bytes memory dst, uint i, string calldata value) internal pure {
865
+ copy(dst, i, Keys.String, bytes(value));
866
+ }
867
+
868
+ /// @dev Encode a two-word composite with a nested BYTES block copied from calldata.
869
+ function copyComposite(
870
+ bytes memory dst,
871
+ uint i,
872
+ bytes4 blockkey,
873
+ uint a,
874
+ uint b,
875
+ bytes calldata value
876
+ ) private pure {
877
+ uint len = max32(64 + Sizes.Header + value.length);
878
+ uint key = uint32(blockkey);
879
+ uint byteskey = uint32(Keys.Bytes);
880
+ assembly ("memory-safe") {
881
+ let p := add(add(dst, 0x20), i)
882
+ mstore(p, or(shl(224, key), shl(192, len)))
883
+ mstore(add(p, 0x08), a)
884
+ mstore(add(p, 0x28), b)
885
+
886
+ let q := add(p, 0x48)
887
+ let valuelen := value.length
888
+ mstore(q, or(shl(224, byteskey), shl(192, valuelen)))
889
+ calldatacopy(add(q, 0x08), value.offset, valuelen)
890
+ }
891
+ }
892
+
893
+ /// @notice Encode a STEP block at `i`, copying its nested input from calldata.
894
+ function copyStep(bytes memory dst, uint i, uint cmd, uint resources, bytes calldata input) internal pure {
895
+ copyComposite(dst, i, Keys.Step, cmd, resources, input);
896
+ }
897
+
898
+ /// @notice Encode a CALL block at `i`, copying its nested payload from calldata.
899
+ function copyCall(bytes memory dst, uint i, uint target, uint resources, bytes calldata payload) internal pure {
900
+ copyComposite(dst, i, Keys.Call, target, resources, payload);
901
+ }
902
+
903
+ /// @notice Encode a RELAY block at `i`, copying its nested input from calldata.
904
+ function copyRelay(bytes memory dst, uint i, uint portal, uint resources, bytes calldata input) internal pure {
905
+ copyComposite(dst, i, Keys.Relay, portal, resources, input);
906
+ }
907
+
908
+ /// @notice Encode a DISPATCH block at `i`, copying its nested payload from calldata.
909
+ function copyDispatch(bytes memory dst, uint i, uint portal, uint resources, bytes calldata payload) internal pure {
910
+ copyComposite(dst, i, Keys.Dispatch, portal, resources, payload);
911
+ }
912
+
913
+ /// @notice Encode a CONTEXT block at `i`, copying its nested streams from calldata.
914
+ function copyContext(
915
+ bytes memory dst,
916
+ uint i,
917
+ bytes32 account,
918
+ bytes calldata state,
919
+ bytes calldata input
920
+ ) internal pure {
921
+ uint len = max32(32 + 2 * Sizes.Header + state.length + input.length);
922
+ uint key = uint32(Keys.Context);
923
+ uint byteskey = uint32(Keys.Bytes);
924
+ assembly ("memory-safe") {
925
+ let p := add(add(dst, 0x20), i)
926
+ mstore(p, or(shl(224, key), shl(192, len)))
927
+ mstore(add(p, 0x08), account)
928
+
929
+ let q := add(p, 0x28)
930
+ let statelen := state.length
931
+ mstore(q, or(shl(224, byteskey), shl(192, statelen)))
932
+ calldatacopy(add(q, 0x08), state.offset, statelen)
933
+
934
+ let r := add(add(q, 0x08), statelen)
935
+ let inputlen := input.length
936
+ mstore(r, or(shl(224, byteskey), shl(192, inputlen)))
937
+ calldatacopy(add(r, 0x08), input.offset, inputlen)
938
+ }
939
+ }
940
+
941
+ /// @notice Encode a RECOVER block at `i`, copying its nested witness from calldata.
942
+ function copyRecover(
943
+ bytes memory dst,
944
+ uint i,
945
+ uint handler,
946
+ uint resources,
947
+ bytes32 recoverykey,
948
+ bytes calldata witness
949
+ ) internal pure {
950
+ uint len = max32(96 + Sizes.Header + witness.length);
951
+ uint key = uint32(Keys.Recover);
952
+ uint byteskey = uint32(Keys.Bytes);
953
+ assembly ("memory-safe") {
954
+ let p := add(add(dst, 0x20), i)
955
+ mstore(p, or(shl(224, key), shl(192, len)))
956
+ mstore(add(p, 0x08), handler)
957
+ mstore(add(p, 0x28), resources)
958
+ mstore(add(p, 0x48), recoverykey)
959
+
960
+ let q := add(p, 0x68)
961
+ let witnesslen := witness.length
962
+ mstore(q, or(shl(224, byteskey), shl(192, witnesslen)))
963
+ calldatacopy(add(q, 0x08), witness.offset, witnesslen)
964
+ }
965
+ }
966
+
830
967
  // -------------------------------------------------------------------------
831
968
  // Calldata decoding
832
969
  // -------------------------------------------------------------------------
@@ -861,16 +998,6 @@ library Blocks {
861
998
  if (read32(abs) != expected) revert UnexpectedValue();
862
999
  }
863
1000
 
864
- /// @notice Read one unsigned integer from an absolute calldata position.
865
- /// @dev DANGER: Unchecked calldata read. Values beyond calldata are zero-padded.
866
- /// @param abs Absolute calldata position.
867
- /// @return value Decoded unsigned integer.
868
- function readUint(uint abs) internal pure returns (uint value) {
869
- assembly ("memory-safe") {
870
- value := calldataload(abs)
871
- }
872
- }
873
-
874
1001
  // Generic block unpackers
875
1002
 
876
1003
  /// @notice Validate a block against `spec` and return its payload.
@@ -1591,58 +1718,146 @@ library Blocks {
1591
1718
  // Block factory helpers
1592
1719
  // -------------------------------------------------------------------------
1593
1720
 
1721
+ /// @dev Allocate an exact-length result with one trailing scratch word for
1722
+ /// unchecked writers that store an eight-byte header with `mstore`.
1723
+ function allocate(uint len) private pure returns (bytes memory value) {
1724
+ value = new bytes(len + 32);
1725
+ assembly ("memory-safe") {
1726
+ mstore(value, len)
1727
+ }
1728
+ }
1729
+
1730
+ // Generic factories
1731
+
1594
1732
  /// @notice Encode a block with a raw payload.
1595
1733
  /// @param key Block type key.
1596
1734
  /// @param payload Raw payload bytes.
1597
- /// @return Encoded block bytes.
1598
- function create(bytes4 key, bytes memory payload) internal pure returns (bytes memory) {
1599
- return bytes.concat(key, bytes4(uint32(payload.length)), payload);
1735
+ /// @return value Encoded block bytes.
1736
+ function create(bytes4 key, bytes memory payload) internal pure returns (bytes memory value) {
1737
+ uint len = max32(payload.length);
1738
+ value = allocate(Sizes.Header + len);
1739
+ write(value, 0, key, payload);
1740
+ }
1741
+
1742
+ /// @notice Encode a block by copying its raw payload from calldata.
1743
+ function createCopy(bytes4 key, bytes calldata payload) internal pure returns (bytes memory value) {
1744
+ uint len = max32(payload.length);
1745
+ value = allocate(Sizes.Header + len);
1746
+ copy(value, 0, key, payload);
1747
+ }
1748
+
1749
+ // Dynamic leaf factories
1750
+
1751
+ /// @notice Encode a LIST block.
1752
+ function list(bytes memory value) internal pure returns (bytes memory blockdata) {
1753
+ uint len = max32(value.length);
1754
+ blockdata = allocate(Sizes.Header + len);
1755
+ writeList(blockdata, 0, value);
1756
+ }
1757
+
1758
+ /// @notice Encode a LIST block by copying its payload from calldata.
1759
+ function listCopy(bytes calldata value) internal pure returns (bytes memory blockdata) {
1760
+ uint len = max32(value.length);
1761
+ blockdata = allocate(Sizes.Header + len);
1762
+ copyList(blockdata, 0, value);
1763
+ }
1764
+
1765
+ /// @notice Encode an EVM block.
1766
+ function evm(bytes memory value) internal pure returns (bytes memory blockdata) {
1767
+ uint len = max32(value.length);
1768
+ blockdata = allocate(Sizes.Header + len);
1769
+ writeEvm(blockdata, 0, value);
1770
+ }
1771
+
1772
+ /// @notice Encode an EVM block by copying its payload from calldata.
1773
+ function evmCopy(bytes calldata value) internal pure returns (bytes memory blockdata) {
1774
+ uint len = max32(value.length);
1775
+ blockdata = allocate(Sizes.Header + len);
1776
+ copyEvm(blockdata, 0, value);
1600
1777
  }
1601
1778
 
1602
1779
  /// @notice Encode a BYTES block with a raw payload.
1603
1780
  /// @param value Raw payload bytes.
1604
- /// @return Encoded BYTES block bytes.
1605
- function data(bytes memory value) internal pure returns (bytes memory) {
1606
- return create(Keys.Bytes, value);
1781
+ /// @return blockdata Encoded BYTES block bytes.
1782
+ function data(bytes memory value) internal pure returns (bytes memory blockdata) {
1783
+ uint len = max32(value.length);
1784
+ blockdata = allocate(Sizes.Header + len);
1785
+ writeBytes(blockdata, 0, value);
1786
+ }
1787
+
1788
+ /// @notice Encode a BYTES block by copying its payload from calldata.
1789
+ function dataCopy(bytes calldata value) internal pure returns (bytes memory blockdata) {
1790
+ uint len = max32(value.length);
1791
+ blockdata = allocate(Sizes.Header + len);
1792
+ copyBytes(blockdata, 0, value);
1607
1793
  }
1608
1794
 
1609
1795
  /// @notice Encode a STRING block with a UTF-8 payload.
1610
1796
  /// @param value String payload.
1611
- /// @return Encoded STRING block bytes.
1612
- function text(string memory value) internal pure returns (bytes memory) {
1613
- return create(Keys.String, bytes(value));
1797
+ /// @return blockdata Encoded STRING block bytes.
1798
+ function text(string memory value) internal pure returns (bytes memory blockdata) {
1799
+ uint len = max32(bytes(value).length);
1800
+ blockdata = allocate(Sizes.Header + len);
1801
+ writeString(blockdata, 0, value);
1802
+ }
1803
+
1804
+ /// @notice Encode a STRING block by copying its payload from calldata.
1805
+ function textCopy(string calldata value) internal pure returns (bytes memory blockdata) {
1806
+ uint len = max32(bytes(value).length);
1807
+ blockdata = allocate(Sizes.Header + len);
1808
+ copyString(blockdata, 0, value);
1614
1809
  }
1615
1810
 
1811
+ // Annotation factories
1812
+
1616
1813
  /// @notice Encode a LABEL block.
1617
1814
  /// @param namespace Label namespace.
1618
1815
  /// @param name Label text.
1619
- /// @return Encoded LABEL block bytes.
1620
- function label(bytes32 namespace, string memory name) internal pure returns (bytes memory) {
1621
- return create(Keys.Label, bytes.concat(namespace, text(name)));
1816
+ /// @return value Encoded LABEL block bytes.
1817
+ function label(bytes32 namespace, string memory name) internal pure returns (bytes memory value) {
1818
+ uint len = max32(Sizes.B32 + bytes(name).length);
1819
+ value = allocate(Sizes.Header + len);
1820
+ writeLabel(value, 0, namespace, name);
1622
1821
  }
1623
1822
 
1624
1823
  /// @notice Encode an ACTION annotation block.
1625
- /// @param value Canonical semantic action identifier.
1626
- /// @return Encoded ACTION block bytes.
1627
- function action(uint value) internal pure returns (bytes memory) {
1628
- return create(Keys.Action, bytes.concat(bytes32(value)));
1824
+ /// @param actionid Canonical semantic action identifier.
1825
+ /// @return value Encoded ACTION block bytes.
1826
+ function action(uint actionid) internal pure returns (bytes memory value) {
1827
+ value = allocate(Sizes.B32);
1828
+ write32(value, 0, Keys.Action, bytes32(actionid));
1629
1829
  }
1630
1830
 
1631
1831
  /// @notice Encode a SCHEMA block.
1632
1832
  /// @param spec Block specification.
1633
1833
  /// @param body Schema body.
1634
1834
  /// @param name Schema name.
1635
- /// @return Encoded SCHEMA block bytes.
1636
- function schema(uint spec, string memory body, bytes32 name) internal pure returns (bytes memory) {
1637
- return create(Keys.Schema, bytes.concat(bytes32(spec), text(body), name));
1835
+ /// @return value Encoded SCHEMA block bytes.
1836
+ function schema(uint spec, string memory body, bytes32 name) internal pure returns (bytes memory value) {
1837
+ uint len = max32(Sizes.B64 + bytes(body).length);
1838
+ value = allocate(Sizes.Header + len);
1839
+ writeSchema(value, 0, spec, body, name);
1638
1840
  }
1639
1841
 
1842
+ // Fixed-width factories
1843
+
1640
1844
  /// @notice Encode a BALANCE block.
1641
1845
  /// @param asset Asset identifier.
1642
1846
  /// @param amount Token amount.
1643
- /// @return Encoded BALANCE block bytes.
1644
- function balance(bytes32 asset, uint amount) internal pure returns (bytes memory) {
1645
- return bytes.concat(Keys.Balance, bytes4(uint32(64)), asset, bytes32(amount));
1847
+ /// @return value Encoded BALANCE block bytes.
1848
+ function balance(bytes32 asset, uint amount) internal pure returns (bytes memory value) {
1849
+ value = allocate(Sizes.Balance);
1850
+ writeBalance(value, 0, asset, amount);
1851
+ }
1852
+
1853
+ /// @notice Encode a CUSTODY block.
1854
+ /// @param host Host node ID holding the custody.
1855
+ /// @param asset Asset identifier.
1856
+ /// @param amount Token amount.
1857
+ /// @return value Encoded CUSTODY block bytes.
1858
+ function custody(uint host, bytes32 asset, uint amount) internal pure returns (bytes memory value) {
1859
+ value = allocate(Sizes.B96);
1860
+ writeCustody(value, 0, host, asset, amount);
1646
1861
  }
1647
1862
 
1648
1863
  /// @notice Encode a POSITION block.
@@ -1650,30 +1865,15 @@ library Blocks {
1650
1865
  /// @param amount Quantity on the asset side.
1651
1866
  /// @param liability Identifier for the liability side.
1652
1867
  /// @param debt Quantity owed on the liability side.
1653
- /// @return Encoded POSITION block bytes.
1868
+ /// @return value Encoded POSITION block bytes.
1654
1869
  function position(
1655
1870
  bytes32 asset,
1656
1871
  uint amount,
1657
1872
  bytes32 liability,
1658
1873
  uint debt
1659
- ) internal pure returns (bytes memory) {
1660
- return bytes.concat(
1661
- Keys.Position,
1662
- bytes4(uint32(128)),
1663
- asset,
1664
- bytes32(amount),
1665
- liability,
1666
- bytes32(debt)
1667
- );
1668
- }
1669
-
1670
- /// @notice Encode a CUSTODY block.
1671
- /// @param host Host node ID holding the custody.
1672
- /// @param asset Asset identifier.
1673
- /// @param amount Token amount.
1674
- /// @return Encoded CUSTODY block bytes.
1675
- function custody(uint host, bytes32 asset, uint amount) internal pure returns (bytes memory) {
1676
- return bytes.concat(Keys.Custody, bytes4(uint32(96)), bytes32(host), asset, bytes32(amount));
1874
+ ) internal pure returns (bytes memory value) {
1875
+ value = allocate(Sizes.Position);
1876
+ writePosition(value, 0, asset, amount, liability, debt);
1677
1877
  }
1678
1878
 
1679
1879
  /// @notice Encode a TRANSACTION block.
@@ -1681,53 +1881,138 @@ library Blocks {
1681
1881
  /// @param to Destination account identifier.
1682
1882
  /// @param asset Asset identifier.
1683
1883
  /// @param amount Transfer amount.
1684
- /// @return Encoded TRANSACTION block bytes.
1685
- function transaction(bytes32 from, bytes32 to, bytes32 asset, uint amount) internal pure returns (bytes memory) {
1686
- return bytes.concat(Keys.Transaction, bytes4(uint32(128)), from, to, asset, bytes32(amount));
1884
+ /// @return value Encoded TRANSACTION block bytes.
1885
+ function transaction(
1886
+ bytes32 from,
1887
+ bytes32 to,
1888
+ bytes32 asset,
1889
+ uint amount
1890
+ ) internal pure returns (bytes memory value) {
1891
+ value = allocate(Sizes.Transaction);
1892
+ writeTransaction(value, 0, from, to, asset, amount);
1687
1893
  }
1688
1894
 
1895
+ // Composite factories
1896
+
1689
1897
  /// @notice Encode a STEP block.
1690
1898
  /// @param cmd Command identifier.
1691
1899
  /// @param resources Packed resources assigned to the step.
1692
1900
  /// @param input Raw nested input payload.
1693
- /// @return Encoded STEP block bytes.
1694
- function step(uint cmd, uint resources, bytes memory input) internal pure returns (bytes memory) {
1695
- return create(Keys.Step, bytes.concat(bytes32(cmd), bytes32(resources), data(input)));
1901
+ /// @return value Encoded STEP block bytes.
1902
+ function step(uint cmd, uint resources, bytes memory input) internal pure returns (bytes memory value) {
1903
+ uint len = max32(Sizes.B64 + Sizes.Header + input.length);
1904
+ value = allocate(len);
1905
+ writeStep(value, 0, cmd, resources, input);
1906
+ }
1907
+
1908
+ /// @notice Encode a STEP block by copying its nested input from calldata.
1909
+ function stepCopy(uint cmd, uint resources, bytes calldata input) internal pure returns (bytes memory value) {
1910
+ uint len = max32(Sizes.B64 + Sizes.Header + input.length);
1911
+ value = allocate(len);
1912
+ copyStep(value, 0, cmd, resources, input);
1696
1913
  }
1697
1914
 
1698
1915
  /// @notice Encode a CALL block.
1699
1916
  /// @param target Target node identifier.
1700
1917
  /// @param resources Packed resources assigned to the call.
1701
1918
  /// @param payload Raw calldata payload for the target.
1702
- /// @return Encoded CALL block bytes.
1703
- function call(uint target, uint resources, bytes memory payload) internal pure returns (bytes memory) {
1704
- return create(Keys.Call, bytes.concat(bytes32(target), bytes32(resources), data(payload)));
1919
+ /// @return value Encoded CALL block bytes.
1920
+ function call(uint target, uint resources, bytes memory payload) internal pure returns (bytes memory value) {
1921
+ uint len = max32(Sizes.B64 + Sizes.Header + payload.length);
1922
+ value = allocate(len);
1923
+ writeCall(value, 0, target, resources, payload);
1705
1924
  }
1706
1925
 
1707
- /// @notice Encode a CONTEXT block.
1708
- /// @param account Command account identifier.
1709
- /// @param state Embedded state block stream.
1710
- /// @param input Embedded input block stream.
1711
- /// @return Encoded CONTEXT block bytes.
1712
- function context(bytes32 account, bytes memory state, bytes memory input) internal pure returns (bytes memory) {
1713
- return create(Keys.Context, bytes.concat(account, data(state), data(input)));
1926
+ /// @notice Encode a CALL block by copying its nested payload from calldata.
1927
+ function callCopy(uint target, uint resources, bytes calldata payload) internal pure returns (bytes memory value) {
1928
+ uint len = max32(Sizes.B64 + Sizes.Header + payload.length);
1929
+ value = allocate(len);
1930
+ copyCall(value, 0, target, resources, payload);
1714
1931
  }
1715
1932
 
1716
1933
  /// @notice Encode a RELAY block.
1717
1934
  /// @param portal Destination portal identifier, often the destination host ID.
1718
1935
  /// @param resources Chain-specific resources for the destination context.
1719
1936
  /// @param input Nested input block stream.
1720
- /// @return Encoded RELAY block bytes.
1721
- function relay(uint portal, uint resources, bytes memory input) internal pure returns (bytes memory) {
1722
- return create(Keys.Relay, bytes.concat(bytes32(portal), bytes32(resources), data(input)));
1937
+ /// @return value Encoded RELAY block bytes.
1938
+ function relay(uint portal, uint resources, bytes memory input) internal pure returns (bytes memory value) {
1939
+ uint len = max32(Sizes.B64 + Sizes.Header + input.length);
1940
+ value = allocate(len);
1941
+ writeRelay(value, 0, portal, resources, input);
1942
+ }
1943
+
1944
+ /// @notice Encode a RELAY block by copying its nested input from calldata.
1945
+ function relayCopy(uint portal, uint resources, bytes calldata input) internal pure returns (bytes memory value) {
1946
+ uint len = max32(Sizes.B64 + Sizes.Header + input.length);
1947
+ value = allocate(len);
1948
+ copyRelay(value, 0, portal, resources, input);
1723
1949
  }
1724
1950
 
1725
1951
  /// @notice Encode a DISPATCH block.
1726
1952
  /// @param portal Destination portal identifier, often the destination host ID.
1727
1953
  /// @param resources Chain-specific resources for the destination dispatch.
1728
1954
  /// @param payload Encoded payload.
1729
- /// @return Encoded DISPATCH block bytes.
1730
- function dispatch(uint portal, uint resources, bytes memory payload) internal pure returns (bytes memory) {
1731
- return create(Keys.Dispatch, bytes.concat(bytes32(portal), bytes32(resources), data(payload)));
1955
+ /// @return value Encoded DISPATCH block bytes.
1956
+ function dispatch(uint portal, uint resources, bytes memory payload) internal pure returns (bytes memory value) {
1957
+ uint len = max32(Sizes.B64 + Sizes.Header + payload.length);
1958
+ value = allocate(len);
1959
+ writeDispatch(value, 0, portal, resources, payload);
1960
+ }
1961
+
1962
+ /// @notice Encode a DISPATCH block by copying its nested payload from calldata.
1963
+ function dispatchCopy(
1964
+ uint portal,
1965
+ uint resources,
1966
+ bytes calldata payload
1967
+ ) internal pure returns (bytes memory value) {
1968
+ uint len = max32(Sizes.B64 + Sizes.Header + payload.length);
1969
+ value = allocate(len);
1970
+ copyDispatch(value, 0, portal, resources, payload);
1971
+ }
1972
+
1973
+ /// @notice Encode a CONTEXT block.
1974
+ function context(
1975
+ bytes32 account,
1976
+ bytes memory state,
1977
+ bytes memory input
1978
+ ) internal pure returns (bytes memory value) {
1979
+ uint len = max32(Sizes.B32 + 2 * Sizes.Header + state.length + input.length);
1980
+ value = allocate(len);
1981
+ writeContext(value, 0, account, state, input);
1982
+ }
1983
+
1984
+ /// @notice Encode a CONTEXT block by copying its nested streams from calldata.
1985
+ function contextCopy(
1986
+ bytes32 account,
1987
+ bytes calldata state,
1988
+ bytes calldata input
1989
+ ) internal pure returns (bytes memory value) {
1990
+ uint len = max32(Sizes.B32 + 2 * Sizes.Header + state.length + input.length);
1991
+ value = allocate(len);
1992
+ copyContext(value, 0, account, state, input);
1993
+ }
1994
+
1995
+ /// @notice Encode a RECOVER block.
1996
+ function recover(
1997
+ uint handler,
1998
+ uint resources,
1999
+ bytes32 recoverykey,
2000
+ bytes memory witness
2001
+ ) internal pure returns (bytes memory value) {
2002
+ uint len = max32(Sizes.B96 + Sizes.Header + witness.length);
2003
+ value = allocate(len);
2004
+ writeRecover(value, 0, handler, resources, recoverykey, witness);
2005
+ }
2006
+
2007
+ /// @notice Encode a RECOVER block by copying its nested witness from calldata.
2008
+ function recoverCopy(
2009
+ uint handler,
2010
+ uint resources,
2011
+ bytes32 recoverykey,
2012
+ bytes calldata witness
2013
+ ) internal pure returns (bytes memory value) {
2014
+ uint len = max32(Sizes.B96 + Sizes.Header + witness.length);
2015
+ value = allocate(len);
2016
+ copyRecover(value, 0, handler, resources, recoverykey, witness);
1732
2017
  }
1733
2018
  }
package/codec/Buffers.sol CHANGED
@@ -6,6 +6,7 @@ import {Cursors} from "../utils/Cursors.sol";
6
6
  /// @title Buffers
7
7
  /// @notice Allocation and finalization helpers for mutable memory byte buffers.
8
8
  /// @dev Packed buffer positions use `Cursors`; flag bit 0 denotes growth policy.
9
+ /// `write` copies from memory, while `copy` copies directly from calldata.
9
10
  library Buffers {
10
11
  using Cursors for uint;
11
12
 
@@ -99,6 +100,19 @@ library Buffers {
99
100
  }
100
101
  }
101
102
 
103
+ /// @notice Copy raw calldata bytes to byte offset `i`.
104
+ /// @param buffer Destination buffer.
105
+ /// @param i Destination byte offset.
106
+ /// @param value Calldata bytes to copy.
107
+ /// @return next Position immediately after the copied bytes.
108
+ function copy(bytes memory buffer, uint i, bytes calldata value) internal pure returns (uint next) {
109
+ next = i + value.length;
110
+ if (next > buffer.length) revert BufferOverflow();
111
+ assembly ("memory-safe") {
112
+ calldatacopy(add(add(buffer, 0x20), i), value.offset, value.length)
113
+ }
114
+ }
115
+
102
116
  /// @notice Write one complete raw word at byte offset `i`.
103
117
  /// @param buffer Destination buffer.
104
118
  /// @param i Destination byte offset.
@@ -62,6 +62,26 @@ library Decoders {
62
62
  cur.state = cur.state.seekAbs(end);
63
63
  }
64
64
 
65
+ /// @notice Validate and enter the payload of the next block in a cursor.
66
+ /// @dev The cursor remains in its existing frame so callers can decode child
67
+ /// blocks in place. Callers should prove complete payload consumption with
68
+ /// `cur.state.expectAbs(end)` after decoding the children.
69
+ /// @param cur Cursor advanced over the block header to its payload.
70
+ /// @param spec Expected parent block specification.
71
+ /// @return abs Absolute position of the first payload byte.
72
+ /// @return end Absolute position immediately after the payload.
73
+ function enter(Cur memory cur, uint spec) internal pure returns (uint abs, uint end) {
74
+ (abs, end) = Blocks.expect(cur.state.absolute(), spec);
75
+ cur.state = cur.state.seekAbs(abs);
76
+ }
77
+
78
+ /// @notice Require a decoder cursor to be at absolute position `abs`.
79
+ /// @param cur Cursor whose position is validated.
80
+ /// @param abs Expected absolute position.
81
+ function expectAbs(Cur memory cur, uint abs) internal pure {
82
+ cur.state.expectAbs(abs);
83
+ }
84
+
65
85
  /// @notice Create a child cursor over relative range `[from, to)`.
66
86
  /// @param cur Parent cursor.
67
87
  /// @param from Inclusive relative start.
@@ -226,6 +246,45 @@ library Decoders {
226
246
  // Execution-compatible fixed-width block decoding
227
247
  // -------------------------------------------------------------------------
228
248
 
249
+ /// @dev Return the next raw calldata word and advance by `size` bytes.
250
+ function next(Cur memory cur, uint size) private pure returns (bytes32 value) {
251
+ uint abs;
252
+ (cur.state, abs) = cur.state.consume(size);
253
+ value = Blocks.read32(abs);
254
+ }
255
+
256
+ /// @notice Return the next raw byte and advance the cursor by one byte.
257
+ function next1(Cur memory cur) internal pure returns (bytes1 value) {
258
+ value = bytes1(next(cur, 1));
259
+ }
260
+
261
+ /// @notice Return the next two raw bytes and advance the cursor by two bytes.
262
+ function next2(Cur memory cur) internal pure returns (bytes2 value) {
263
+ value = bytes2(next(cur, 2));
264
+ }
265
+
266
+ /// @notice Return the next four raw bytes and advance the cursor by four bytes.
267
+ function next4(Cur memory cur) internal pure returns (bytes4 value) {
268
+ value = bytes4(next(cur, 4));
269
+ }
270
+
271
+ /// @notice Return the next eight raw bytes and advance the cursor by eight bytes.
272
+ function next8(Cur memory cur) internal pure returns (bytes8 value) {
273
+ value = bytes8(next(cur, 8));
274
+ }
275
+
276
+ /// @notice Return the next sixteen raw bytes and advance the cursor by sixteen bytes.
277
+ function next16(Cur memory cur) internal pure returns (bytes16 value) {
278
+ value = bytes16(next(cur, 16));
279
+ }
280
+
281
+ /// @notice Return the next raw calldata word and advance the cursor.
282
+ /// @param cur Cursor advanced by one word.
283
+ /// @return value Raw word at the cursor's previous position.
284
+ function next32(Cur memory cur) internal pure returns (bytes32 value) {
285
+ value = next(cur, Sizes.Word);
286
+ }
287
+
229
288
  /// @notice Decode one fixed 32-byte payload described by `spec`.
230
289
  /// @param cur Cursor advanced past the block.
231
290
  /// @param spec Expected block specification.