저는 현재 Dapp 프로젝트 ( Shape )를 작업 중입니다. 그의 첫 번째 주요 개발 단계가 끝나고 있습니다. 거래 비용은 항상 개발자에게 큰 관심사이므로이 기사를 사용하여이 분야의 지난 몇 주간 / 월간에 얻은 통찰력을 최적화 측면에서 공유하고자합니다.
https://cdn-images-1.medium.com/max/720/0*N0ODhAxqWx2iLANx
에 의해 "100 개 달러 지폐의 근접 촬영 사진" 페피 Stojanovski 에 Unsplash
아래에서는 최적화 기술 목록을 제시합니다.이 중 일부는 주제에 대한 자세한 기사를 참조하여 계약 설계에 적용 할 수 있습니다. 나는 좀 더 기본적인, 오히려 익숙한 개념부터 시작해서 더 복잡한 과정을 밟을 것이다.
목차
- 선호하는 데이터 유형
- 계약의 바이트 코드에 값 저장
- SOLC를 통해 단일 슬롯에 변수 패킹
- 어셈블리와 함께 단일 슬롯에 변수 패킹
- 함수 매개 변수 연결
- 스토리지 부하 감소를위한 Merkle 증명
- 무국적 계약
- IPFS에 데이터 저장
- 선호하는 데이터 유형
몇 단어로 대답 할 수 있습니다 : 256 비트 변수 , ergo256 및 bytes32를 사용하십시오! 처음에는 반 직관적 인 것처럼 보이지만 EVM (Ethereum Virtual Machine)이 작동하는 방식에 대해 자세히 생각해 보면 완전히 이해할 수 있습니다. 각 저장소 슬롯에는 256 비트가 있습니다. 따라서 uint8 만 저장하는 경우 EVM은 누락 된 모든 자릿수를 0으로 채 웁니다.이 비용은 가스 비용입니다. 또한 EVM에 의해 uint256에서 계산이 수행되므로 uint256 이외의 다른 유형도 변환해야합니다.
참고 : 일반적으로 전체 스토리지 슬롯이 채워지도록 변수의 크기를 조정해야합니다. " SOLC를 통해 단일 슬롯에 변수를 패킹 "절에서 256 비트보다 작은 변수를 사용하는 것이 더 명확 해집니다.
- 계약의 바이트 코드에 값 저장
정보를 저장하고 읽는 비교적 저렴한 방법은 스마트 계약을 블록 체인에 배포 할 때이를 스마트 계약의 바이트 코드에 직접 포함시키는 것입니다. 단점은 나중에 값을 변경할 수 없다는 것입니다. 그러나 데이터를로드하고 저장하는 데 필요한 가스 소비량이 크게 줄어 듭니다. 이를 구현하는 데는 두 가지 방법이 있습니다.
Variablr 선언에 키워드 상수 를 첨부하십시오.
변수를 사용하고자하는 위치에 하드 코드하십시오.
uint256 public v1;
uint256 public 상수 v2;
함수 calculate ()는 (uint256 결과)를
반환합니다. return v1 * v2 * 10000
}
변수 v1 은 계약 상태의 일부이고 v2 및 1000 도 계약의 바이트 코드의 일부입니다.
(v1 읽기는 이미 200 개의 가스만으로 SLOAD 작업을 통해 수행됩니다.)
- SOLC를 통해 단일 슬롯에 변수 패킹
블록 체인에 영구적으로 데이터를 저장하는 경우 어셈블리 명령 인 SSTORE가 백그라운드에서 실행됩니다. 이것은 가스 비용이 20,000 인 가장 비싼 명령이므로 가능한 한 적게 사용해야합니다. 구조체 내부에서 수행되는 SSTORE 연산의 양은 다음 예제에서와 같이 변수를 간단히 재정렬함으로써 줄일 수 있습니다.
struct 데이터 {
uint64 a;
uint64 b;
uint128 c;
uint256 d;
}
데이터 공개 데이터;
생성자 (uint64 _a, uint64 _b, uint128 _c, uint256 _d) public {
Data.a = _a;
Data.b = _b;
Data.c = _c;
Data.d = _d;
}
여기에서 구조체 내에서 256 비트 슬롯을 채울 수있는 모든 변수는 서로 인접하여 정렬되므로 나중에 컴파일러에서 함께 스택 할 수 있습니다 (변수가 256 비트 미만인 경우에도 작동 함). 이 특정 예에서 SSTORE 작업은 a , b 및 c 를 저장하기 위해 한 번, d 를 저장하기 위해 한 번만 두 번 사용됩니다 . 구조체 외부의 변수에도 동일하게 적용됩니다. 또한 동일한 슬롯에 여러 변수를 저장하면 절약 효과가 전체 슬롯을 채워서 얻는 것보다 훨씬 큽니다 (기본 데이터 유형).
참고 : SOLC 최적화를 활성화하십시오.
- 어셈블리와 함께 단일 슬롯에 변수 패킹
실행될 SSTORE 작업이 적어 지도록 변수를 함께 쌓아 올리는 기법을 수동으로 적용 할 수도 있습니다. 다음 코드는 uint64 유형의 4 개 변수를 하나의 단일 256 비트 슬롯에 함께 스택합니다.
인코딩 : 변수를 하나로 병합합니다.
함수 인코딩 (UINT64 _a, UINT64 _B, UINT64 _c, UINT64 _d) 내부의 순수 반환 (bytes32의 X) {
조립체 {
예를 보자 = 0
mstore (0x20에, _d)
mstore (0x18, _c)
mstore (0x10, 명령 _B)
mstore를 ( 0x8, _a)
x : = mload (0x20)
}
}
독서를 위해,이 두 번째 기능으로 실현 될 수있는 변수를 해독해야합니다.
디코드 : 변수를 초기 부분으로 분할합니다.
함수의 디코드 (bytes32 x) 내부 순수 리턴 (uint64 a, uint64 b, uint64 c, uint64 d) {
어셈블리 {
d : = x
mstore (0x18, x)
a : = mload (0)
mstore (0x10, x)
b : = mload (0)
mstore (0x8, x)
c : = mload (0)
}
}
이 방법의 가스 소비량과 위의 가스 소비량을 비교해 보면 다음과 같은 이유로 인해이 값이 훨씬 저렴하다는 것을 알 수 있습니다.
정밀도 : 이 방법을 사용하면 비트 패킹과 관련하여 거의 모든 작업을 수행 할 수 있습니다. 예를 들어, 여러분이 이미 알고 있다면, 변수의 마지막 비트는 필요하지 않을 것이고, 256 비트 변수와 함께 사용하는 1 비트 변수를 추가함으로써 쉽게 최적화 할 수 있습니다.
한 번 읽기 : 변수가 실제로 단일 슬롯에 함께 저장되므로 모든 변수를 수신하기 위해 한 번의로드 작업 만 수행하면됩니다. 변수가 함께 사용될 경우 특히 유용합니다.
그래서, 왜 이전의 것을 사용합니까? 두 가지 구현을 모두 살펴보면 변수 를 추출 하고 해독 하기 위해 어셈블리를 사용하여 가독성을 포기하고 있으므로이 두 번째 방법은 오류가 발생하기 쉽습니다. 우리는 포함해야하기 때문에 또한, 엉 - 및 디코딩 각각의 특정 경우에 대한 기능을 배포 비용도 크게 증가 할 것이다. 그럼에도 불구하고 실제로 가스 소비량을 줄이려면이 방법이 필요합니다. 단일 슬롯에 압축하는 변수가 많을수록 다른 방법에 비해 절감 효과가 커집니다.
- 함수 매개 변수 연결
데이터를 읽고 저장하는 프로세스를 최적화하기 위해 위에서 설명한 en 및 decode 함수를 사용할 수있는 것처럼 호출 데이터로드를 줄이기 위해 함수 호출의 매개 변수를 연결하는데도 사용할 수 있습니다. 이로 인해 거래의 실행 비용이 약간 증가하지만 기본 수수료가 줄어들어 합리적으로 비용이 절감됩니다.
이 기사에서는이 기술이없는 두 가지 함수 호출 (비트 압축)을 비교하여 여기에서 실제로 발생하는 작업을 완벽하게 설명합니다.
- 스토리지 부하 감소를위한 Merkle 증명
요컨대, merkle 증명은 훨씬 더 많은 양의 데이터의 유효성을 증명하기 위해 단일 데이터 덩어리를 사용합니다.
Merkle 교정의 아이디어에 익숙하지 않은 경우 먼저 다음 기사를 검토하여 기본적인 이해를 얻으십시오.
Merkle 교정과 함께 제공되는 이점은 정말 놀랍습니다. 예제를 살펴 보겠습니다.
예를 들어, 32 개의 구성을 모두 포함하는 자동차 구매 트랜잭션을 저장하려고한다고 가정합니다. 32 개의 변수로 구성된 구조체를 생성하면 각 구성마다 하나의 변수가 매우 비쌉니다! 이것은 merkle 증명이 들어오는 곳입니다 :
먼저, 어떤 정보가 함께 요청되고 그에 따라 32 개의 속성을 그룹화하는지 살펴 봅니다. 일을 간단하게하기 위해 각각 8 개의 구성을 포함하는 4 개의 그룹을 찾았다 고 가정합니다.
이제 우리는 내부의 데이터로부터 4 개의 그룹 각각에 대한 해시를 생성하고 이전의 기준에 따라 다시 그룹화합니다.
해시가 하나만 남을 때까지 이것을 반복합니다. merkle-root (hash1234).
자동차 예제 Merkle-Tree
두 요소가 동시에 사용되는지 여부에 따라 우리가 그룹화하는 이유는 각 확인을 위해 해당 분기의 모든 요소 (다이어그램에서 색상이 지정됨)가 필요하며 자동으로 검증되기 때문입니다. 이는 하나의 확인 프로세스 만 필요하다는 것을 의미합니다. 예를 들면 :
핑크색 요소에 대한 Merkle-proof
여기서 체인에 저장해야하는 것은 머클 루트 (merkle-root)입니다 (보통 256 비트 변수 (keccak256)). 그러나 자동차 제조업체가 잘못된 색상의 자동차를 전송한다고 가정하면 이것이 자동차가 아니라는 것을 쉽게 증명할 수 있습니다. 주문했다.
bytes32 public merkleRoot;
// a, ..., h를 주황색 기본 블록이라고합시다.
함수 검사
(
bytes32 hash4,
bytes32 hash12,
uint256 a,
uint32 b,
bytes32 c,
문자열 d,
문자열 e,
bool f,
uint256 g,
uint256 h
)
public view returns (bool success)
{
bytes32 hash3 = keccak256 (abi.encodePacked (a, b, c, d, e, f, g, h));
bytes32 hash34 = keccak256 (abi.encodePacked (hash3, hash4));
요구 (keccak256 (abi.encodePacked (hash12, hash34)) == merkleRoot, "잘못된 요소");
참을 돌려라.
Keep in Mind : 특정 변수가 매우 자주 액세스되거나 수시로 변경되어야하는 경우, 기존의 방식으로이 특정 값을 저장하는 것이 더 합리적 일 수 있습니다. 또한,이 트랜잭션에 사용할 수있는 스택 슬롯의 수를 초과 할 것이므로 지점이 너무 커지지 않도록주의하십시오.
- 무국적 계약
무국적 계약은 트랜잭션 데이터 및 이벤트 호출과 같은 항목이 블록 체인에 완전히 저장된다는 사실을 이용합니다. 따라서 계약 상태를 계속 변경하는 대신 트랜잭션을 보내고 저장하려는 값을 전달하면됩니다. SSTORE 작업은 일반적으로 거래 비용의 대부분을 차지하기 때문에 무국적 계약은 상태 저장 계약의 가스 분율만을 소비합니다. 다음 기사에서는 무국적 계약의 개념과 백 엔드 계약을 만드는 방법을 완벽하게 설명합니다.
이것을 위에서 자동차 예제에 적용하면 paramers 함수를 연결할 수 있는지 여부에 따라 하나 또는 두 개의 트랜잭션을 전송합니다 (5. 함수 매개 변수 연결) . 우리는 32 개의 자동차 구성을 전달합니다. 우리가 외부에서 정보를 확인하기 만하면, 이것은 잘 작동하고 Merkle 증명보다 약간 더 쌀 수도 있습니다. 그러나 반면에 중앙 집중화, 비용 또는 사용자 경험 측면에서 희생하지 않으면 서 계약 내에서 이러한 정보에 액세스하는 것은 사실상 불가능합니다.
- IPFS에 데이터 저장
IPFS 네트워크는 각 파일이 URL을 통해 식별되는 것이 아니라 내용의 해시를 통해 식별되는 분산 된 데이터 저장소입니다. 이점은 해시를 변경할 수 없으므로 특정 해시가 항상 동일한 파일을 가리 킵니다. 따라서 IPFS 네트워크에 데이터를 브로드 캐스트 한 다음 계약서에 해당 해시를 저장하여 나중에 정보를 참조 할 수 있습니다. 이 작동 방식에 대한 자세한 설명은이 기사에서 찾을 수 있습니다.
오프 체인 데이터 저장 : Ethereum 및 IPFS
, gas
medium.com에
저장
무국적 계약처럼이 방법은 실제로 스마트 계약 (Oracles과 함께 가능) 내에서 실제로 데이터를 사용하는 것을 허용하지 않습니다. 여전히, 특히 비디오와 같이 대용량의 데이터를 저장하려는 경우에는이 방법이 최선의 방법입니다.
6, 7, 8의 유스 케이스가 상당히 비슷하기 때문에, 다음과 같은 사용법에 대한 요약이 있습니다.
Merkle-trees : 중소 규모의 데이터. / 계약 내에서 데이터를 사용할 수 있습니다. / 데이터 변경은 다소 복잡합니다.
상태없는 계약 : 중소 규모 데이터. / 계약 내에서 데이터를 사용할 수 없습니다. / 데이터를 변경할 수 있습니다.
IPFS : 대량의 데이터. / 계약 내에서 데이터를 사용하는 것은 상당히 번거롭다 / 데이터를 변경하는 것은 다소 복잡합니다.