본문 바로가기
  • 인공지능
  • 블록체인
  • 정보보안
신기술분석/블록체인

ICP 실습 #3

by nathan03 2023. 10. 21.
반응형

# Reference APIs

init (초기 값 설정)

let user: Opt<typeof User> = None;
let reaction: Opt<typeof Reaction> = None;
let owner: Opt<Principal> = None;

export default Canister({
    init: init( [User, Reaction, Principal], (initUser, initReaction, initOwner) => {
            user = Some(initUser);
            reaction = Some(initReaction);
            owner = Some(initOwner);
        }
    ),
    getUser: query([], Opt(User), () => {
        return user;
    }),
    getReaction: query([], Opt(Reaction), () => {
        return reaction;
    }),
    getOwner: query([], Opt(Principal), () => {
        return owner;
    }),
});


배포

dfx deploy {캐니스터명} --argument '(record { id = "0" }, variant { Fire }, principal "rrkah-fqaaa-aaaaa-aaaaq-cai")'


# arg data raw (아큐먼트를 바이트타입으로 변환하여 반환)

import { blob, bool, Canister, ic, int8, query, text } from 'azle';

export default Canister({
    // returns the argument data as bytes.
    argDataRaw: query(
        [blob, int8, bool, text],
        blob,
        (arg1, arg2, arg3, arg4) => {
            return ic.argDataRaw();
        }
    ), 
    argDataRawSize: query(
        [blob, int8, bool, text],
        nat,
        (arg1, arg2, arg3, arg4) => {
            return ic.argDataRawSize();
        }
    )
});


# cross canister

코드

  • index.ts
import { Canister, ic, Principal, query, Record, text, update, Vec } from 'azle';
import SubCanister from './sub';

const Wrapper = Record({
    sub : SubCanister 
});

export default Canister({
    canisterParam: query([SubCanister ], SubCanister , (sub) => {
        return sub ;
    }),
    canisterReturnType: query([], SubCanister , () => {
        return SubCanister (
            Principal.fromText(
                process.env.SOME_CANISTER_PRINCIPAL ??
                    ic.trap('process.env.SOME_CANISTER_PRINCIPAL is undefined')
            )
        );
    }),

    canisterNestedReturnType: update([], Wrapper, () => {
        return {
            sub : SubCanister (
                Principal.fromText(
                    process.env.SOME_CANISTER_PRINCIPAL ??
                        ic.trap(
                            'process.env.SOME_CANISTER_PRINCIPAL is undefined'
                        )
                )
            )
        };
    }),
    canisterList: update([Vec(SubCanister )], Vec(SubCanister ), (sub ) => {
            return sub ;
        }
    ),

   crossUpdate: update([SubCanister ], text, async (sub ) => {
        return await ic.call(sub.update1);
    },

		crossQuery: query( [SubCanister], bool, async (sub) => {
            return await ic.call(sub.query1);
        }
    ),

});

sub.ts

import { bool, Canister, query, text, update } from 'azle';

export default Canister({
    query1: query([], bool, () => {
        return true;
    }),
    
    update1: update([], text, () => {
        return 'SomeCanister update1';
    })
});

sub 캐니스터 생성을 진행

sub 캐니스터 생성을 진행

파일 구성 확인

index.did

service: () -> {
    canisterCrossCanisterCall: (service {query1: () -> (bool) query; update1: () -> (text);}) -> (bool) query;
    canisterList: (vec service {query1: () -> (bool) query; update1: () -> (text);}) -> (vec service {query1: () -> (bool) query; update1: () -> (text);});
    canisterNestedReturnType: () -> (record {subCanister:service {query1: () -> (bool) query; update1: () -> (text);}});
    canisterParam: (service {query1: () -> (bool) query; update1: () -> (text);}) -> (service {query1: () -> (bool) query; update1: () -> (text);}) query;
    canisterReturnType: () -> (service {query1: () -> (bool) query; update1: () -> (text);}) query;
}


sub.did

service: () -> {
    query1: () -> (bool) query;
    update1: () -> (text);
}

dfx.json

{
    "canisters": {
        "{메인-캐니스터}": {
            "type": "custom",
            "main": "src/index.ts",
            "candid": "src/index.did",
            "build": "npx azle types",
            "wasm": ".azle/types/types.wasm",
            "gzip": true,
            "declarations": {
                "output": "temp/dfx_generated/types",
                "node_compatibility": true
            },
            "env": ["SUB_PRINCIPAL"]
        }, 
        "sub" : {
            "type": "custom",
            "main": "src/sub.ts",
            "candid": "src/sub.did",
            "build": "npx azle sub",
            "wasm": ".azle/sub/sub.wasm",
            "gzip": true,
            "declarations": {
                "output": "temp/dfx_generated/sub",
                "node_compatibility": true
            } 
        }
    }
}

배포

  • 메인 캐니스터 배포
npx azle {메인-캐니스터}
  • sub 캐니스터 배포 - 프로젝트 폴더내 .azle 구성을 먼저 진행해야함 배포 진행 dfx.json의 env 변수를 위해 - 위 sub 캐니스터의 id 입력
SUB_PRINCIPAL="{sub 캐니스터 입력}" dfx deploy

# candid UI - 브라우저 확인

결과 확인

Candid UI상 서비스 객채가 파라메터로 들어 가는 경우, 인코딩의 오류로 dfx로 결과확인이 간편

  • canisterList 호출
  • canisterParam 호출

  • crossQuery 호출
  • crossUpdate 호출

 

call

import { Canister, ic, init, nat64, Principal, update } from 'azle';

const TokenCanister = Canister({
    transfer: update([Principal, nat64], nat64)
});

let tokenCanister: typeof TokenCanister;

export default Canister({
    init: init([], setup),
    postDeploy: init([], setup),
    payout: update([Principal, nat64], nat64, async (to, amount) => {
        return await ic.call(tokenCanister.transfer, {
            args: [to, amount]
        });
    }), 
		executeCallRaw: update(
        [Principal, text, text, nat64],
        text,
        async (canisterId, method, candidArgs, payment) => {
            const candidBytes = await ic.callRaw(
                canisterId,
                method,
                ic.candidEncode(candidArgs),
                payment
            );

            return ic.candidDecode(candidBytes);
        }
    ), 
		executeInstallCode: update(
        [Principal, blob],
        Void,
        async (canisterId, wasmModule) => {
            return await ic.call(managementCanister.install_code, {
                args: [
                    {
                        mode: { install: null },
                        canister_id: canisterId,
                        wasm_module: wasmModule,
                        arg: Uint8Array.from([])
                    }
                ],
                cycles: 100_000_000_000n
            });
        }
    ),
});

function setup() {
    tokenCanister = TokenCanister(
        Principal.fromText('r7inp-6aaaa-aaaaa-aaabq-cai')
    );
}

caller

import { Canister, ic, Principal, update } from 'azle';

export default Canister({
    // returns the principal of the identity that called this function
    caller: update([], Principal, () => {
        return ic.caller();
    })
});

 

# 캐니스터 메소드

heartbeat

import { Canister, heartbeat } from 'azle';

export default Canister({
    heartbeat: heartbeat(() => {
        console.log('this runs ~1 time per second');
    })
});

http

import { blob, bool, Canister, Func, nat16, None, Opt, query, Record,
	text, Tuple, Variant, Vec } from 'azle';

const Token = Record({
    // add whatever fields you'd like
    arbitrary_data: text
});

const StreamingCallbackHttpResponse = Record({
    body: blob,
    token: Opt(Token)
});

export const Callback = Func([text], StreamingCallbackHttpResponse, 'query');

const CallbackStrategy = Record({
    callback: Callback,
    token: Token
});

const StreamingStrategy = Variant({
    Callback: CallbackStrategy
});

type HeaderField = [text, text];
const HeaderField = Tuple(text, text);

const HttpResponse = Record({
    status_code: nat16,
    headers: Vec(HeaderField),
    body: blob,
    streaming_strategy: Opt(StreamingStrategy),
    upgrade: Opt(bool)
});

const HttpRequest = Record({
    method: text,
    url: text,
    headers: Vec(HeaderField),
    body: blob,
    certificate_version: Opt(nat16)
});

export default Canister({
    http_request: query([HttpRequest], HttpResponse, (req) => {
        return {
            status_code: 200,
            headers: [],
            body: Buffer.from('hello'),
            streaming_strategy: None,
            upgrade: None
        };
    }),
		http_request_update: update([HttpRequest], HttpResponse, (req) => {
        return {
            status_code: 200,
            headers: [],
            body: Buffer.from('hello'),
            streaming_strategy: None,
            upgrade: None
        };
    })
});

post upgrade

import { Canister, postUpgrade } from 'azle';

export default Canister({
    postUpgrade: postUpgrade([], () => {
        console.log('This runs after every canister upgrade');
    })
});

 

# 캐니스터 메니지먼트

canister_status

import { Canister, ic, update } from 'azle';
import { CanisterStatusArgs, CanisterStatusResult, managementCanister
} from 'azle/canisters/management';

export default Canister({
    getCanisterStatus: update([CanisterStatusArgs], CanisterStatusResult, async (args) => {
            return await ic.call(managementCanister.canister_status, {
                args: [args]
            });
        }
    )
});


create_canister

import { Canister, ic, None, update } from 'azle';
import {
    CreateCanisterResult,
    managementCanister
} from 'azle/canisters/management';

export default Canister({
    executeCreateCanister: update([], CreateCanisterResult, async () => {
        return await ic.call(managementCanister.create_canister, {
            args: [{ settings: None }],
            cycles: 50_000_000_000_000n
        });
    })
});

deposit_cycles

import { bool, Canister, ic, Principal, update } from 'azle';
import { managementCanister } from 'azle/canisters/management';

export default Canister({
    executeDepositCycles: update([Principal], bool, async (canisterId) => {
        await ic.call(managementCanister.deposit_cycles, {
            args: [
                {
                    canister_id: canisterId
                }
            ],
            cycles: 10_000_000n
        });

        return true;
    })
});

start

import { bool, Canister, ic, Principal, update } from 'azle';
import { managementCanister } from 'azle/canisters/management';

export default Canister({
    executeStartCanister: update([Principal], bool, async (canisterId) => {
        await ic.call(managementCanister.start_canister, {
            args: [
                {
                    canister_id: canisterId
                }
            ]
        });

        return true;
    })
});

Stop

import { bool, Canister, ic, Principal, update } from 'azle';
import { managementCanister } from 'azle/canisters/management';

export default Canister({
    executeStopCanister: update([Principal], bool, async (canisterId) => {
        await ic.call(managementCanister.stop_canister, {
            args: [
                {
                    canister_id: canisterId
                }
            ]
        });

        return true;
    })
});

delete_canister

import { bool, Canister, ic, Principal, update } from 'azle';
import { managementCanister } from 'azle/canisters/management';

export default Canister({
    executeDeleteCanister: update([Principal], bool, async (canisterId) => {
        await ic.call(managementCanister.delete_canister, {
            args: [
                {
                    canister_id: canisterId
                }
            ]
        });

        return true;
    })
});


ecdsa_public_key

import { blob, Canister, ic, None, Record, update } from 'azle';
import { managementCanister } from 'azle/canisters/management';

const PublicKey = Record({
    publicKey: blob
});

export default Canister({
    publicKey: update([], PublicKey, async () => {
        const caller = ic.caller().toUint8Array();

        const publicKeyResult = await ic.call(
            managementCanister.ecdsa_public_key,
            {
                args: [
                    {
                        canister_id: None,
                        derivation_path: [caller],
                        key_id: {
                            curve: { secp256k1: null },
                            name: 'dfx_test_key'
                        }
                    }
                ]
            }
        );

        return {
            publicKey: publicKeyResult.public_key
        };
    })
});

http_request

import { Canister, ic, None, Principal, query, Some, update } from 'azle';
import {
    HttpResponse,
    HttpTransformArgs,
    managementCanister
} from 'azle/canisters/management';

export default Canister({
    xkcd: update([], HttpResponse, async () => {
        return await ic.call(managementCanister.http_request, {
            args: [
                {
                    url: `https://xkcd.com/642/info.0.json`,
                    max_response_bytes: Some(2_000n),
                    method: {
                        get: null
                    },
                    headers: [],
                    body: None,
                    transform: Some({
                        function: [ic.id(), 'xkcdTransform'] as [
                            Principal,
                            string
                        ],
                        context: Uint8Array.from([])
                    })
                }
            ],
            cycles: 50_000_000n
        });
    }),
    xkcdTransform: query([HttpTransformArgs], HttpResponse, (args) => {
        return {
            ...args.response,
            headers: []
        };
    })
});

raw_rand

import { blob, Canister, ic, update } from 'azle';
import { managementCanister } from 'azle/canisters/management';

export default Canister({
    getRawRand: update([], blob, async () => {
        return await ic.call(managementCanister.raw_rand);
    })
});

update setting

import { bool, Canister, ic, None, Principal, Some, update } from 'azle';
import { managementCanister } from 'azle/canisters/management';

export default Canister({
    executeUpdateSettings: update([Principal], bool, async (canisterId) => {
        await ic.call(managementCanister.update_settings, {
            args: [
                {
                    canister_id: canisterId,
                    settings: {
                        controllers: None,
                        compute_allocation: Some(1n),
                        memory_allocation: Some(3_000_000n),
                        freezing_threshold: Some(2_000_000n)
                    }
                }
            ]
        });

        return true;
    })
});

 

# 메모리

structures

import { bool, Canister, nat64, nat8, Opt, query, StableBTreeMap, text, Tuple,
    update, Vec } from 'azle';

const Key = nat8;
const Value = text;

let map = StableBTreeMap(Key, Value, 0);

export default Canister({
    containsKey: query([Key], bool, (key) => {
        return map.containsKey(key);
    }),
    get: query([Key], Opt(Value), (key) => {
        return map.get(key);
    }),
    mapInsert: update([Key, Value], Opt(Value), (key, value) => {
        return map.insert(key, value);
    }),
    isEmpty: query([], bool, () => {
        return map.isEmpty();
    }),
    items: query([], Vec(Tuple(Key, Value)), () => {
        return map.items();
    }),
    keys: query([], Vec(Key), () => {
        return map.keys();
    }),
    len: query([], nat64, () => {
        return map.len();
    }),
    mapRemove: update([Key], Opt(Value), (key) => {
        return map.remove(key);
    }),
    values: query([], Vec(Value), () => {
        return map.values();
    })
});

bytes

import { blob, Canister, ic, query } from 'azle';

export default Canister({
    stableBytes: query([], blob, () => {
        return ic.stableBytes();
    })
});

grow

import { Canister, ic, nat32, update } from 'azle';

export default Canister({
    stableGrow: update([nat32], nat32, (newPages) => {
        return ic.stableGrow(newPages);
    }),
		stable64Grow: update([nat64], nat64, (newPages) => {
        return ic.stable64Grow(newPages);
    })
});

read

import { blob, Canister, ic, nat32, query } from 'azle';

export default Canister({
    stableRead: query([nat32, nat32], blob, (offset, length) => {
        return ic.stableRead(offset, length);
    }),
		stable64Read: query([nat64, nat64], blob, (offset, length) => {
        return ic.stable64Read(offset, length);
    })
});

size

import { Canister, ic, nat32, query } from 'azle';

export default Canister({
    stableSize: query([], nat32, () => {
        return ic.stableSize();
    }),
		stable64Size: query([], nat64, () => {
        return ic.stable64Size();
    })
});

write

import { blob, Canister, ic, nat32, update, Void } from 'azle';

export default Canister({
    stableWrite: update([nat32, blob], Void, (offset, buf) => {
        ic.stableWrite(offset, buf);
    }),
		stable64Write: update([nat64, blob], Void, (offset, buf) => {
        ic.stable64Write(offset, buf);
    })
});


# Timer

import { Canister, ic, TimerId, update, Void } from 'azle';

export default Canister({
    clearTimer: update([TimerId], Void, (timerId) => {
        ic.clearTimer(timerId);
    }),
		setTimers: update([Duration], Tuple(TimerId, TimerId), (delay) => {
        const functionTimerId = ic.setTimer(delay, callback);

        const capturedValue = '🚩';

        const closureTimerId = ic.setTimer(delay, () => {
            console.log(`closure called and captured value ${capturedValue}`);
        });

        return [functionTimerId, closureTimerId];
    }),
		setTimerIntervals: update(
        [Duration],
        Tuple(TimerId, TimerId),
        (interval) => {
            const functionTimerId = ic.setTimerInterval(interval, callback);

            const capturedValue = '🚩';

            const closureTimerId = ic.setTimerInterval(interval, () => {
                console.log(
                    `closure called and captured value ${capturedValue}`
                );
            });

            return [functionTimerId, closureTimerId];
        }
    ),
});

function callback() {
    console.log('callback called');
}
반응형

'신기술분석 > 블록체인' 카테고리의 다른 글

ICP 실습 #2  (0) 2023.10.23
ICP 실습 #1  (1) 2023.10.21
우분투 Geth 설치 하기  (1) 2022.06.12
Pinata IPFS 서비스  (0) 2022.06.12
오픈 재플린  (0) 2022.06.05

댓글