안녕하세요 제가 bapp을 제작 중 이였는데 문제가 발생하여 몇가지 질문을 남기려고 합니다.
일단 단순히 설명 드리면 매 초, 변경되어질 수 있는 숫자들을 총합하여 해당 값에 대한 평균을 구하려고 했습니다.
solidity에서 사용 가능한 loop문인 for, do, do while문을 이용하여 해당 계산을 진행하려고 하였으나,
전부다 gas limit에 걸려서 실패하였습니다.
계산 수식의 문제가 아니란걸 확인하고 싶어서 반복문 안에서 어떠한 계산 없이 단순히 loop를 돌리는경우
약 3만회정도의 loop에서 오류가 발생하였으며, gas limit을 줄 수 있는 범위까지 넣었으나 전부 실패하였습니다.
단순 view나 pure함수의 경우 잘 작동하지만 state값을 변경하는 external, public같은 함수들의 경우 실패하는것으로 확인되었습니다.
혹시 klaytn 네트워크에서 반복문을 제한없이 실행할 수 있는 방법이 있을까요?
안녕하세요
solidity안에서 모든 데이터를 처리하는것은 무리일것으로 보입니다.
제한없이 반복할 수 있는 방법보다 최적화를 고려하시는게 좋을것같습니다.
최적화 방법으로는 pagination, mapping을 이용하는 방법이 있을 수 있겠습니다.
해당 데이터를 여러번 fetch하여 별도의 database에 저장을 하고
일부 바뀔 수 있는 데이터는 주기적으로 갱신을 해주셔야합니다.
추가적으로 특정 데이터가 바뀌거나 생성될때 event를 만들고
api에서 watch하여 바로 변경사항을 반영하게 할 수 있습니다.
2개의 좋아요
@rimose
오류가 나는 트랜잭션에 대한 가스비 소모 - Denver님의 글 #2 글을 참고해보시면 도움이 될 거 같습니다.
요약하여 답변 드리면, klaytn 네트워크에서 반복문을 제한없이 실행할 수 있는 방법은 없습니다. 있으면 곤란합니다.
Klaytn 과 같은 블록체인을 직접 개발한다고 생각해보시면, 무한 루프를 실행 가능하게 한다는 건 노드가 사용 가능한 컴퓨팅 리소스를 모두 소진하는 형태의 공격이 이루어질 수 있다는 걸 금방 아실 수 있습니다.
블록체인은 특정 주체 또는 집단만을 위한 플랫폼이 아닙니다. 따라서 네트워크 자원을 독식할 수 있는 여지를 남겨서는 안되겠죠?
이러한 관점에서 생각해보시면 좋을 거 같습니다.
감사합니다.
2개의 좋아요
무한 loop실행시 문제가 발생할 수 있는 부분은
어떤 프로그래밍을 하더라도 주의하여야 하는 부분이기에
해당 네트워크의 공격수단으로 쓰일 수 있는 loop문의 반복은 어느정도 막혀야 한다고 생각합니다.
다만 3만번은 너무 적은것 같아서요…
스케일업같이 용량이나 사양의 증대같은 기능이 있는지 궁금해서 여쭤보았습니다…
제가 제작하고 있는 bapp에서 각 유저별로 서로다른 계산이 되어야 해서 해당 유저가
다른 사람들의 계산을 어느정도 진행한다면 그것또한 많은 가스비와 반복문을 실행하여 같은 오류를 발생할것이기 때문에
차마 방법이 없어서 답답한마음에 글 써봤습니다 ㅠㅠ
개인적으로 3만번은 적다고 생각하지않습니다.
mapping과 cache를 이용하면 충분히 구현가능하다고 생각합니다.
그래도 3만번 이상 사용해야한다면 tps가 높고 가스비가 저렴한
다른 블록체인을 사용하는것도 좋은 방법일거라 생각됩니다.
클레이튼은 이더리움이랑 다르게 가스비가 고정적이고 매우 저렴한 편입니다.
이더리움에서는 가스비가 너무 비싸 ERC721을 개선한 ERC721A를 제작한 NFT 프로젝트가 있습니다.
ERC721A
2개의 좋아요
@rimose
계산에 대한 증명 정도만 On Chain에서 하는 걸 고려해보시기 바랍니다.
모든 계산과정을 다 On Chain에서 수행할 필요는 개인적으론 없다고 생각합니다.
그럴 수 있다면 하겠지만, 3만 번의 루프는 과하다고 생각이 드네요.
타 블록체인 사례를 모두 조사해본 것은 아니지만,
설령 3만 번을 처리할 수 있다 하더라도 비용이 매우 비쌀 것으로 예상이 됩니다.
온 체인 상에서의 연산은 최소로 하시고 증명을 온체인에서 하시는 방법을 고려해보시기 바랍니다.
@margintop3498 답변에 도움 주셔서 정말 감사합니다
2개의 좋아요
cache에 대해 말씀하셔서 질문이 있습니다.
매초 랜덤하게 바뀌어질 수 있는 숫자 A가 있고, 이는 매 초 무조건 바뀌는 숫자가 아니기에 A에 대한 값이 변경될때마다
mapping 을 사용하여 해당 숫자를 mapping(시간 => A) 값으로 저장하였습니다.
이때 B라는 참여자들은 각기 다른 시간을 배정받습니다. (B의 인원은 약 1~2만명 사이)
이때 각기다른 B가 자신이 배정받은 시간부터 일정 시간만큼 평균을 계산한다면,
배정받은 시간부터, 매 초 mapping된 값이 변경되었음을 감지하기 위한 수단으로
반복문을 수행하여야 하고, 약 10일에 대한 A의 평균을 구한다고 하면 초단위 연산이기에 864000번 loop를 돌아야 하고, A의 값은 평균적으로 10초마다 한번씩 바뀌었다고 가정하면 약 86400번 계산되어야 할것입니다.
또한 B의 참여 시점이 각기 다르기때문에 서로다른 B’1, B’2, B’3 … B’n 이라는 사람들에 대해
계산을 따로 해줄 수 밖에 없는 상황입니다.
이런 상황에서 cache를 적용한다면 어떤 방법으로 캐싱데이터를 쌓아야할지에 대한 좋은 방향이 있을까요?
넵 현재 어떤것을 구현하고 싶은지 자세하게 설명해주시면
답변해드리도록 하겠습니다.
1개의 좋아요
저도 그러고 싶은데… 모종의 이유로
블록체인 위에서 모든 동작 및 처리를 수행하여만 하는 상황이 발생하였습니다…
어떤 방법이 없을까요?
정확하게 파악하기가 힘들지만
보내주신 내용을 컨트랙트 안에서만 구현하기에는 무리가 있어보입니다.
추가적인 답변을 하기위해 몇가지 질문을 남겨봅니다.
-
A값을 매초 바꾸기위해 변경하는 컨트랙트를 계속 실행시켜주시는것인가요?
-
B의 A에대한 평균을 계산하는 코스트는 시간이 지남에따라 계속 증가하게될텐데 이부분은 생각해보신걸까요?
아래와 같은 방법이 원하시는 방법인지는 모르겠지만
저렴한 비용으로 유저별 평균을 구할 수 있습니다.
(오류 케이스나, 유저가 생성되지않은 경우, 유저를 여러번 생성할 수 있는 케이스는 직접 대응 해주셔야합니다.)
pragma solidity 0.5.6;
import "hardhat/console.sol";
import "@openzeppelin/contracts/ownership/Ownable.sol";
contract Game is Ownable {
uint256 public sum;
uint256 public tick;
uint256 public index;
struct User {
uint256 joinedSum;
uint256 joinedIndex;
}
mapping(address => User) users;
mapping(uint256 => uint256) ticks;
constructor() public {
}
function updateTick(uint256 newTick) external onlyOwner {
sum += newTick;
tick = newTick;
ticks[index] = tick;
index++;
}
function createUser() public {
User memory user;
user.joinedSum = sum;
user.joinedIndex = index;
users[msg.sender] = user;
}
function getTickAverage() public view returns (uint256) {
return sum / index;
}
function getMyAverage() public view returns (uint256) {
User memory user = users[msg.sender];
return (sum - user.joinedSum) / (index - user.joinedIndex);
}
}
1개의 좋아요
추가적으로 EN을 직접 운영하신다면
--opcode-computation-cost-limit
옵션을 추가해 최대 코스트를 변경할 수 있는것으로 보입니다.
다만 해당 옵션은 실험적인 기능이니 참고 부탁드립니다.
1개의 좋아요
A1. 네 다만 주체가 컨트랙트 오너가 변경하는것이 아니라 B참여자의 행동등에 의해 변경이 됩니다.
A2. 네, 이부분이 구조를 설계할때 제일 문제되는 부분입니다…
코드에 index부분이 초단위로 진행되어야 해서요… 위의 예시대로
B’1이 배정받은 시간부터 A가 100이 9초동안 유지되었고 10초가 되는 순간에 0이 되었다면 A값의 평균은
(9100 + 01 / 10) 으로 90이 나와야 합니다.