SteemAPI getAccountHistory 사용법과 엄청 중요한 팁

in #kr-dev6 years ago



Prologue


본래 시작은 ["불금의 이생각 저생각"]에서 였습니다.
그 중에서 타로점 분석은 image까지 포함해서 릴리즈를 했고, 또 다른 아이디어 인 스팀 수익 계산기가 이 대단한 삽질의 시작이었습니다.

"기존의 tool을 찾아보면 pending payout만 계산해 주고, 이미 payout된 것은 계산해 주는 기능이 없는 것 같아서, 직접 만들어 볼려고 합니다. 혹시 이미 알고 계신것이 있다면 댓글 부탁 드립니다."

없는 것에는 다 이유가 있는 것이었죠.

스팀 수익 계산기를 만들어서 "짠"하고 포스팅 해야지 하는 바람은 수많은 삽질의 시작이었습니다.



1차 시도


일단 간단한 PoC(Proof Of Concept)를 만들어 보기로 했습니다. Steemd나 혹은 스팀의 계정 정보에 보면
그동안의 history가 쭈욱 나옵니다.

그리고 payout이 되면 reward라고 나오구요...

이걸 보다 보니 reward도 궁금하지만, 그동안 제가 거래소에서 구매해서 steem으로 전송한 금액을 얼마나 될까
궁금해 지더라구요. 그래서...잠시 PoC도 할겸 거래소에서 전송한 총 금액이 얼마인지 계산하는 프로그램을 작성하였습니다.

Steem API를 뒤져보니 getAccountHistory라는 것이 있더라구요.

그 형태는 아래와 같이 생겼습니다.

Get Account History

steem.api.getAccountHistory(account, from, limit, function(err, result) {
  console.log(err, result);
});

그래서 이걸 이용하면 되겠지라고 생각하고 무작정 try를 시작했습니다.

위의 API는 node.js에서 사용하는 형태이고, 유사하게 restful API로 사용할수도 있습니다. 사실 이번에 작업을 하면서 node.js API를 보니 안에 특별한 내용이 있는 것은 아니고 restful API와의 Map을 하는 정도더라구요. 물론 그래도 이렇게 되어 있는 편이 사용하기 훨씬 편합니다.

위 API의 사용은 굉장히 쉬워 보였습니다.

steem.api.getAccountHistory('jeaimetu',3,1,(err, result) => {
    console.log("3 1 test");
    console.log(result);
});

이렇게 하면, 결과로 가져온 전체 array를 다 찍어 줍니다. 참 쉽죠?


[[0,{"trx_id":"b18df85ef82a8664ddce00d744f377ea00fcb267","block":19412593,"trx_in_block":4,"op_in_trx":0,"virtual_op":0,"timestamp":"2018-01-29T21:07:09","op":["account_create_with_delegation",{"fee":"0.500 STEEM","delegation":"29700.000000 VESTS","creator":"steem","new_account_name":"jeaimetu","owner":{"weight_threshold":1,"account_auths":[],"key_auths":[["STM7sr17ckJ4aHdotvpKT1adtxRfCmLE2KjLJKYoVbFAdN8rwpwW2",1]]},"active":{"weight_threshold":1,"account_auths":[],"key_auths":[["STM5iGV2H9DTrNzPkeAATxek5Nisq9F8JnSgtj52jnbdegyY74uCU",1]]},"posting":{"weight_threshold":1,"account_auths":[],"key_auths":[["STM5ccv6pCTXPNZptn3TsH2Ate4F3gqPQTkk6yTVSTczfhQj3MY8J",1]]},"memo_key":"STM7wfXfnXmWHSt4k9H1ZnCX7q2v3AXUvywS5Ds7u4JpXivED19gG","json_metadata":"","extensions":[]}]}]]

대략 이런 내용입니다. 별로 도움이 안되는 내용 같지만, 그 중에서 의미가 있는 것은

"op"뒤에 나오는 내용입니다.

,"op":["account_create_with_delegation",

Operation을 의미하고, 이건 제 계정의 제일 첫번째 record로 계정 생성을 의미합니다. 여기에 steem을 주고 받은 것은 transfer로, 글이나 curation에 대한 보상은 reward등으로 표시가 됩니다.

대략의 의미는 From이 3이니까 3번째 record부터 Limit=1, 즉 최대 1개를 가져와라, 모 이런 뜻입니다.

그런데..

steem.api.getAccountHistory('jeaimetu',1,1,(err, result) => {
    console.log("1 1 test");
    console.log(result[0][0]);
    console.log(result[0][1]);  
    console.log(result);
});

steem.api.getAccountHistory('jeaimetu',2,1,(err, result) => {
    console.log("2 1 test");
    console.log(result[0][0]);
    console.log(result[0][1]);  
    console.log(result);
});

steem.api.getAccountHistory('jeaimetu',3,1,(err, result) => {
    console.log("3 1 test");
    console.log(result[0][0]);
    console.log(result[0][1]);  
    console.log(result);
});

steem.api.getAccountHistory('jeaimetu',2,2,(err, result) => {
    console.log("2 2 test");
    console.log(result[0][0]);
    console.log(result[0][1]);  
    console.log(result);
});

위에 있는 code들의 결과 값이 모두 똑같습니다. 이로?대로라면 1번째부터 1개.....제일 마지막의 것은 2번째부터 2개 이렇게 가져와야 하는데 말이죠..

이때부터 맨붕에 빠집니다.

너무 연속으로 call을 해서 서버가 오류에 빠졌나?

Promise나 Async-await를 써야 하나?

등등..



2차 시도 - Deep Dive


아주 여러가지 시도를 해 보았습니다. 아래 코드는 전체 history를 가져와서 upbit, korbit 및 gopax에서
받은 steem의 양을 계산해줄 것 같은 code입니다.

async function getFullAccountHistory(){
    const end = 9999 * 10;
    const step = 9999;
    var amount = 0;
    for(let start = 0; start < end;start += step) {
        await steem.api.getAccountHistoryAsync('jeaimetu', start, step).each((history: any[]) => {
            const result = history;
        }
        //console.log(err, result);
        const WALLET_FILTER = 'transfer'
        let transfers = result.filter( tx => tx[1].op[0] === WALLET_FILTER )
        //console.log(transfers)
        
    
        transfers.forEach((tx) => {
            if(tx[1].op[1].from == "upbit-exchange" || tx[1].op[1].from == "korbit2" || tx[1].op[1].from == "gopax"){
                console.log(tx[1].op[0], tx[1].op[1].from, tx[1].op[1].amount)
                let money = tx[1].op[1].amount.split(" ");
                amount += parseInt(money[0],10);}
        }
    
        console.log("total amount from exchange", amount);
    
        if(result.length < 1)
            return;
    });
    } //end of for
//} while(result.length != 0)
} //end of async function

요로코럼 온갖 디버그 문과 async-await로 바꾸어서도 해보고, 무슨 짓을 해도....제일 첫번째 계정을 생성한 것에 대한 record만 나오고, 그 뒤의 record가 구해지지 않습니다.

그래서 이번에는...혹시나 하여 이렇게 해 보았습니다.

steem.api.getAccountHistory('jeaimetu',100,50,(err, result) => {
    console.log("2 1 test");
    console.log(result[0][0]);
    console.log(result[0][1]);  
    console.log(result);
});

의미는 100번 record로부터 50개를 가져와라, 이렇게 해석될 수 있습니다만...
node.js가 실행되면서 assert error를 뱉어 냅니다. From이 Limit보다 커야 한다구요..

이게 뭥미?

열심히 구글링을 해보아도 getAccountHistory에 대한 글은 많이 없습니다. 그리고 거기에 있는 것 처럼 따라해도
역시 위에 문제는 해결이 되지 않습니다. 심지어 githut의 issue게시판을 검색해도요...

다행히 구글링을 하다가 From이 Limit보다 커야 하는 것에 대해서는 답을 찾았습니다. 흔히 생각하는 From이 start point이기는 한데, 리턴해 주는 결과값의 제일 마지막 위치를 의미합니다. 즉 From = 2000번이라고 주면, 2000번부터 그 밑에 있는 record를 return하는 것입니다.

이쯤 되면 슬슬 짜증이 나고, 무언가 삽질의 끝을 보고 싶어 집니다. 심지어 삽질도 귀찮아 지구요..
그래서 개발 환경을 Browser로 전환을 합니다. Chrome을 키고 살포시 F12를 눌러 줍니다.

차근 차근 trial을 해보았습니다.

  1. Browser에서 해도 node.js로 한것과 같이 data를 구해오지 못합니다. 무조건 첫 record만 가져옵니다.
  1. 그래서 조금 다른 trial을 해봅니다. 우선 제 account의 전체 history의 개수를 구합니다.
https://api.steemjs.com/get_account_history?account=jeaimetu&from=-1&limit=0

이렇게 하면 전체 record의 개수를 return해 줍니다.

첫번째 array의 다시 첫번째 값이 5514이고, 이 값이 제 account history의 전체 개수 입니다. 앞으로 계속 늘어나겠죠.
가입한지 72일 밖에 안되었는데 5514개이니, 하루 평균 76.5개의 record가 생성된 것입니다. Upvote이던 글 작성이던 reward이던 follow이던 등등등 모두 다 합쳐서요...

이렇게 일단 전체 record를 알았으니 이번에는 역으로 뒤에서부터 조회를 해보았습니다.

https://api.steemjs.com/get_account_history?account=jeaimetu&from=5514&limit=100

어랏..

잘 됩니다. 눈여겨 보셔야 할 부분은 array가 2개로 나뉘는데, 0~99까지의 array와 5514번 array가 분리되어 있습니다.

역시 맨붕입니다. 앞에서 부터 하면 안되고, 뒤에서 부터 하면 됩니다...헐.

이번에는

https://api.steemjs.com/get_account_history?account=jeaimetu&from=1000&limit=100

이렇게 1000번부터 해보니 안됩니다.

그래서..

https://api.steemjs.com/get_account_history?account=jeaimetu&from=2000&limit=100

2000번부터 조회하니 됩니다...

이번엔 limit을 늘려봅니다.

https://api.steemjs.com/get_account_history?account=jeaimetu&from=2000&limit=500

되긴 하는데..어랏?...1716번이 마지막입니다...

어찌된 걸까요?

열심히 github를 뒤지다 보니..아래와 같은 소스의 주석을 발견하였습니다.

https://github.com/MattyIce/postpromoter/blob/master/postpromoter.js

        // Removed this for now since api.steemit.com is not returning more than 30 days of account history!
    //if(state.version != version)
    //  updateVersion(state.version, version);

아....."not returning more than 30 days".....

이렇게 삽질은 끝이 났습니다. 진작에 누가 알려 주었으면 좋았을 것을...



Epilogue


  1. Steemd에서 보이는 transfer, vote, reward등의 transaction에 대해서는 getAccountHistory API를 사용합니다.

  2. getAccountHistory의 parameter는 account, from, limit이 들어가게 되고

  3. Account는 조회하고자 하는 계정의 이름, from에는 transaction의 시작번호, limit에는 조회하고자 하는 최대 record의 수가 들어갑니다.

  4. Transaction은 최대 30일치만 조회가 됩니다. 또한 transction은 From으로 시작하여 생성의 역순으로 조회가 됩니다. From이 100이고 limit이 10이면 100, 99, 98.. 이렇게 조회가 됩니다.

  5. Limit이 10개면 전체 반환되는 값은 11개 입니다. 2000번부터 10개라면 1990부터 2000까지 반환이 됩니다.

  6. API에는 특수 parameter가 있습니다. from에 -1이 들어가면 가장 최신의 것부터 시작을 하게 됩니다.

  7. 이를 응용하여 from에 -1, limit에 0을 주면 가장 마지막 record번호를 조회할 수 있습니다.

그래서 결론은..

모든 transaction을 restful API or node.js steem library를 통해서 조회할 수 없습니다.


Dear Steemian and Developers,

I want to share know how about using getAccountHistory steem API. I translate only final result to share right usage of API to prevent time consuming trual.

  1. If you want to retrieve upvote, transfer, reward and so forth, then you can use "getAccountHistory" API. This retrieve account history as shown as steemd.com

  2. Parameters are account, from and limit. Account is the steemit account name what you want to retrieve. From is the starting number of record and limit is the limitation of the number of query.

  3. VERY IMPORTANT : you can only retrieve records during 30 Days.

  4. Retrieved transaction is descending order. So the number assigned to "From" meant Top number. If you set from = 100 and limit = 10, then result is 100, 99,....., 90. This is very important. Also "From" should be greater than "Limit".

Furthur more, The number of returned array size is not 10 but 11.

  1. There are special parameters for this API.
  • From = -1 meant retrieve from the recent one.
  • From = -1, limit = 0, retreive the latest(most recent) record number.

So the result is...

You can read all transaction through result API or node.js steem library because it limits the query date range within 30 days.

BR, Teddy.

Sort:  

30일 제한... 이것 때문에 저도 한참 삽질했는데ㅎㅎ 스팀 API 문서가 좀 더 자세히 언급하면 좋았을텐데 말이죠

ㅋ. 정말 야속한 스팀잇입니다. 덕분에...공부는 많이 했습니다.

30일 제한이라니.. 좋은정보 감사합니다. 비슷한증상을 몇변 겪었는데 이유가 있었군요.

좀 내용을 찾아보니..이렇게 되어 있습니다.

_prune가 true일때 30개와 30일중에서 먼저 조건에 걸리는 것까지 제거를 합니다.

즉 30일 안에 record가 300개이면 30일까지 data를 자릅니다.
그런데 60일안에 record가 30개이면, 30개에서 data를 자릅니다. 그러니까, 실제 데이터가 거의 업는 계정은 굉장히 오랫동안의 기록을 가져올수도 있습니다.

https://github.com/steemit/steem/blob/1cfdf8101ec415156b155c9ec90b0a4d439a039f/libraries/plugins/account_history/account_history_plugin.cpp

while( seq_itr->account == item
               && sequence - seq_itr->sequence > 30
               && now - _db.get< chain::operation_object >( seq_itr->op ).timestamp > fc::days(30) )

Github history에서 이유를 찾아보려고 했는데, 내용이 너무 많아서 포기했습니다.

저는 getDiscussionsbycreated 를 이용했는데 특정 태그의 포스팅이 30일 이후로는 데이터가 거의 안나오더군요. 확인해보지 않았지만 대부분 api 가 비슷한 형식으로 되어있을것 같습니다.

get_discussions_by_author 였나.. 이름 까먹었는데 작성자의 포스팅 정보 가져오는 api 는 전부가져오길래 상관없는줄 알았는데. 말씀하신 갯수 조건에 해당되서 가져올수 있는거 같네요.

다시한번 감사드립니다.

잘봤습니다. 30 일 이전의 것을 가져오는 방법이 궁금해지네요.

저두요..그건 steem의 opensource를 보고 연구를 하거나 steemd page를 웹 크롤링 해야 할 것 같습니다.

Congratulations! This post has been upvoted from the communal account, @minnowsupport, by jeaimetu from the Minnow Support Project. It's a witness project run by aggroed, ausbitbank, teamsteem, theprophet0, someguy123, neoxian, followbtcnews, and netuoso. The goal is to help Steemit grow by supporting Minnows. Please find us at the Peace, Abundance, and Liberty Network (PALnet) Discord Channel. It's a completely public and open space to all members of the Steemit community who voluntarily choose to be there.

If you would like to delegate to the Minnow Support Project you can do so by clicking on the following links: 50SP, 100SP, 250SP, 500SP, 1000SP, 5000SP.
Be sure to leave at least 50SP undelegated on your account.

많은 도움 되었습니다. 감사합니다.

아, 그리고 조회가 10,000개씩밖에 안돼서... 그 이상의 데이터를 가지신 분들은 뺑뺑이 돌려야겠네요;

소스를 보니 10,000개가 맞습니다. 마지막의 것을 +1하는지 혹은 정말 10,000을 count하는지는 확인을 못했습니다.

DEFINE_API_IMPL( account_history_api_impl, get_account_history )
{
FC_ASSERT( args.limit <= 10000, "limit of ${l} is greater than maxmimum allowed", ("l",args.limit) );
FC_ASSERT( args.start >= args.limit, "start must be greater than limit" );

확실하게 확인해 주셔서 감사합니다. :)
소스까지 열어보시다니 치밀하시네요!
제가 본받을 부분인 것 같습니다.

도움 되셨다니 다행입니다. 맞습니다. 마지막 limit parameter의 최대값이 9,999입니다.

from. limit 숫자는 배열이라서 배열 index 번호로
from = 3 => 배열 0~3까지 4개
limit = 2 => 배열 0-2까지 3개
배열의 index 번호라서 갯수가 참 혼동을 야기한 것 같아요.

Coin Marketplace

STEEM 0.18
TRX 0.13
JST 0.029
BTC 58009.23
ETH 3063.14
USDT 1.00
SBD 2.34